/
Tags: компьютерные технологии вычислительная техника микропроцессоры программирование языки программирования издательство радио и связь язык программирования форт
ISBN: 5-256-00438-7
Year: 1993
Text
М. КЕЛЛИ
Н.СПАЙС
FORTH
A Text
and
Reference
М. КЕЛЛИ
Н.СПАЙС
ЯЗЫК
ПРОГРАММИРОВАНИЯ
<POPT
РЛПТМ А Text and Reference
^^ ^^ Вт I ^TI Mahlon G. Kelly, Nicholas Spies
PRENTICE-HALL, Englewood Cliffs, N.J. 07632
М. КЕЛЛИ
Н.СПАЙС
ЯЗЫК
ПРОГРАММИРОВАНИЯ
ТОРГ
Перевод с английского
E. В. Куркова, Ю. А. Семенова
Москва
"РАДИО И СВЯЗЬ"
1993
ББК 32.97
К 34
УДК 681.325.5
Федеральная целевая программа книгоиздания России
Редакция литературы по вычислительной технике и информатике
Келли М., Спайс H.
К 34 Язык программирования Форт: Пер. с англ. — М.: Радио и связь,
1993. — 320 c.: ил.
ISBN 5-256-00438-7.
В книге американских авторов описаны основные версии языка Форт, включая
стандарты 1979 и 1983 гг., а также версию MMSFORTH для персональной ЭВМ
типа IBM PC. Рассмотрены процедуры для работы с файлами и
последовательностями символов, техника расширения списка стандартных
операторов, а также возможность включения программ, написанных на языке
ассемблера. Приведено большое число примеров, представляющих практический
интерес.
Для программистов.
„ 2404010000 —044 , м А,
046 (01) —93 3 5 7 93
ББК 32.97
ISBN 5-256 -00438-7 (рус.)
ISBN 0-13-326331-2 01 (англ.)
1986 by Prentice-Hall А Division of Simon & Schuster,
Inc. Englewood Cliffs. New Jersey 07632
i Перевод на русский язык, предисловие к русскому
изданию, примечания. Курков E. B., Семенов Ю. А.
1993.
ВВЕДЕНИЕ
В этом разделе содержится важная информация о том, как пользо¬
ваться книгой. Мы надеемся, что вы его обязательно прочтете.
Форт — это мощный язык программирования, который обеспечивает высокую производитель¬
ность ЭВМ. Предлагаемая книга является одновременно учебником и справочником, позволяющим
овладеть языком Форт независимо от того, начинающий вы программист или опытный. Вы можете
пользоваться этой книгой как учебником для классных или индивидуальных занятий, при этом не
предполагается знание основ ЭВМ или программирования. Книга содержит полное описание языка,
начинающееся с упрощенного изложения принципов работы компьютеров. Постепенно осуществля¬
ется переход к более сложным понятиям, не описанным в других книгах. Вы сами можете выбрать
материал и скорость изучения с учетом своего уровня подготовки. В книге приводится множество
примеров, показывающих стиль программирования на языке Форт, включая полный текст програм¬
мы редактора, большое количество упражнений для приобретения опыта, уверенности и, что более
важно, полезных технических навыков. В приложении даны ответы к упражнениям. Излагаются
обе версии языка Форт в соответствии со стандартами 1979 и 1983 г. Кроме того, изложены вопро¬
сы, не освещаемые в стандартах, в том числе организация файлов данных, обработка алфавитно-
цифровых текстов, арифметик4 с плавающей запятой, а также различные инструментальные про¬
граммные средства, что поможет вам расширить сферу применения языка Форт для ваших нужд.
Даны примеры функций усовершенствованной реализации языка MMSFORTH.
Материал книги построен для использования ее в качестве справочника так, чтобы охватить весь
набор средств и приемов и полный перечень слов и функций языка. Подробное содержание каждой
главы дается в разделе содержания и в начале каждой главы и кроме того, имеется сводка перекре¬
стных ссылок для облегчения поиска нужной информации. Приведены также два очень подробных
глоссария: слов языка программирования Форт (включая стандартные, а также расширяющие слова
и слова, определенные в книге) и терминологии компьютеров и понятий, относящихся к языку
Форт. Приводимый материал и его организация должны сделать эту книгу подручным средством
для программирующих на языке Форт.
Что же представляет собой язык Форт? Основная его часть — это набор слов, или словарь, из ко¬
торого слово вызывается по имени для выполнения специфических функций. Слова языка Форт +, -
, * и /, соответственно складывают, вычитают, умножают и делят два числа. Программа на Форте
пишется путем определения новых слов с использованием слов, ранее уже определенных в словаре.
Как только новые слова скомпилированы в словарь, они не отличаются по форме от тех слов, кото¬
рые в нем уже имеются. Программа на Форте — это не более чем расширение самого языка, это
резко отличает его от таких языков программирования, как Бейсик, Фортран или Паскаль, в кото¬
рых язык транслирует всю программу в закодированную форму, понятную компьютеру, не меняя
самого языка. И вследствие того, что Форт использует словарь, новые слова и программы требуют
гораздо меньше памяти и могут выполняться так же быстро, как эквивалентные им программы на
других языках.
Кроме того, языком Форт очень легко пользоваться. Подобно Бейсику, он сразу же реагирует на
любую команду (слово), введенную с клавиатуры. Однако новые слова Форта и программы компи¬
лируются в словарь, поэтому они могут выполняться с той же скоростью или быстрее, чем, напри¬
мер, на Фортране, который к тому же при трансляции программ порождает большие объемы ма¬
шинного кода. Компиляция программы на языке Форт делается значительно быстрее и проще, чем
на Фортране, Коболе, Паскале и других языках.
Есть у Форта и другие своеобразные черты. Почти все языки программирования используют
средства операционной системы (например, СР/М, MS-DOS) для обеспечения ввода информации с
клавиатуры, вывода ее на экран дисплея, сохранения программы на диске и других функций. В
противоположность этому многие версии языка Форт служат сами себе и операционной системой,
или, иначе говоря, все функции операционной системы могут быть написаны на языке Форт и
включены в качестве части его словаря. И в то время как другие языки для сохранения программы
и данных пользуются именованными фашиши на дисках, Форт сохраняет такую же информацию в
нумерованных блоках емкостью 1024 байта каждый. Хотя и в языке Форт могут использоваться
файлы, а сам он может работать с верхним уровнем операционной системы, Форт выполняет функ¬
ции операционной системы специфическим и необычным образом.
5
В большинстве языков программирования требуется, чтобы числа хранились в виде переменных.
В Форте также есть именованные переменные, но для хранения чисел, так же как и для передачи
их из одного слова в другое, в основном используется стек, позволяющий экономить много времени
(числа не приходится выбирать из памяти). Применение стека является как раз той новинкой, ко¬
торую нужно прежде всего освоить в языке Форт.
Форт — структурированный язык. Такие языки, как Фортран и Бейсик, допускают написание
программы с переходами из одного места в другое, что потенциально может привести к путанице и
беспорядку. Некоторые структурные языки, например Паскаль, заставляют разрабатывать програм¬
му в логической последовательности, но делается это с помощью очень строгих правил, которые ог¬
раничивают программиста более жестко, чем на языке Форт. Разработка программ на Форте и их
организация производятся.обычно одновременно на двух уровнях. На верхнем уровне программист
разрабатывает план того, что программа должна делать, пользуясь часто логической блок-схемой
программы. На нижнем уровне программа пишется с помощью определения слов, необходимых для
выполнения задач верхнего уровня. Каждое определяемое слово может быть проверено и отлажено
отдельно или в сочетании с другими словами, что приводит к существенному сокращению времени
разработки и тестирования программы.
Именно возможность писать короткие определения слов и программ и делает язык Форт легким
для изучения. Новое определение может быть введено с клавиатуры и немедленно проверено. Поэ¬
тому Форт можно и нужно изучать на практике методом ”проб и ошибок”. И хотя Форт заключает
в себе некоторые тонкости и абстракции, можно изучить лишь небольшую их часть, чтобы писать
полезные программы. Вы сможете писать программы и экспериментировать с ними раньше, чем
дойдете до середины первой главы. Для облегчения обучения очень важны немедленная реакция и
подтверждение, и только два языка — Бейсик и Лого приближаются к Форту в этом отношении. На
протяжении всей книги мы будем разбирать небольшие примеры и делать упражнения. Вам следует
все их попробовать решить, но еще большему вы научитесь, если будете сами ставить и решать
свои собственные задачи.
И все же многие утверждают, что Форт труден для изучения. Для этого имеется несколько при¬
чин. Опытным программистам Форт зачастую дается труднее, чем новичкам, потому что он отлича--
ется от других языков программирования по самой своей природе. Хотя в языке Форт нет каких-ли-
бо присущих только ему сложностей, программисты с трудом отвыкают от переменных, подпрог¬
рамм, многословного текста на исходном языке, алгебраических обозначений и прочих атрибутов
привычных им языков. Если вы знаете другие языки программирования, попробуйте к языку Форт
подойти с полной отдачей. Освойте понятия стека и определения слов, прежде чем переходить к бо¬
лее сложным вопросам. Забудьте всякие предубеждения, которые у вас могут возникнуть, вроде то¬
го, что для хорошего языка программирования обязательно нужна операционная система и файло¬
вая поддержка. И не беспокойтесь о блок-схеме, начните с небольших задач, ваш опыт будет на¬
капливаться постепенно. Форт может показаться трудным, так как это достаточно мощное средство
программирования.
Действительно, все возможности языка изучить трудно, но все они и не потребуются, чтобы пи¬
сать очень полезные программы. На Форте можно научиться писать программы на уровне хорошего
программиста, пользующегося языками Бейсик и Фортран, быстрее, чем на любом другом языке.
Так же несложно программирование на языке Форт-ассемблера и определение слов Форта в машин¬
ных кодах. Можно описать слова, которые будут создавать в словаре совершенно новые типы дан¬
ных, использовать Форт для модификации самого языка, для операций с большими объемами дан¬
ных в памяти компьютера и даже для того, чтобы реализовать новые языки программирования. Ко¬
нечно, изучение этих вопросов может быть трудным. И, хотя мы осветим большую их часть, вы
сможете хорошо программировать на языке Форт раньше, чем овладеете ими всеми. Форт предо¬
ставляет вам мощные средства для управления работой компьютера, присущие другим языкам
программирования, включая машинный язык, но вам эти мощные средства скорее всего не по¬
требуются.
Существует одна причина, из-за которой Форт иногда оказывается действительно трудным. Дело
в том, что в стандартах языка и в поставляемых потребителям реализациях языка отсутствуют не¬
которые слова для выполнения основных или важных функций, предусмотренных другими языка¬
ми. В таких случаях программист вынужден сам написать базовые слова, которые должны выпол¬
нять эти важные функции. Например, стандартный Форт не содержит операций над числами с пла¬
вающей запятой, в нем нет трансцендентных функций (тригонометрических, логарифмической); не
определены стандартами операции с символами и символьными строками (например, извлечение
отдельных слов из текста), работа с файлами данных, графические возможности. Нет в стандарте и
6
слова, позволяющего вводить числа в процессе исполнения программы. К счастью, во многих по¬
ставляемых реализациях Форта предусмотрены слова, позволяющие преодолеть эти ограничения, а
Форт настолько мощный, что позволяет самому написать такие слова, если знать, как это сделать.
И мы вас этому научим.
Подводя итог, можно сказать, что Форт — это в то же время мощный и неразвитый язык. Мощ¬
ный он потому, что программы, написанные на нем, занимают мало места в памяти и исполняются
с такой же скоростью или быстрее, чем на других языках, он дает потенциально неограниченные
возможности управления компьютером, и писать и отлаживать программы довольно несложно. В то
же время стандартный Форт неразвит, ввиду того что не предусматривает выполнения некоторых
важных функций, которые являются неотъемлемой частью других языков и доступны для програм¬
миста. Введение этих функций предоставлено программисту или предусматривается в некоторых
версиях Форта. Почему же стандартный Форт так бедно определен? Чтобы понять это, надо немно¬
го познакомиться с историей его создания.
Немного истории
В отличие от других языков программирования Форт не является плодом коллективного труда
какого-либо комитета или коллектива ученых, он родился в голове одного человека — Чарльза X.
Мура. В начале 60-х годов Мура стало все больше не удовлетворять время и затраты труда, требо¬
вавшиеся для разработки программ на существовавших тогда ЭВМ. В течение нескольких лет он со¬
здал основы прототипа языка Форт, пользуясь для этого такими языками, как Алгол, Кобол, PL/1
и ассемблер для IBM/360. Мур дал своему языку название FORTH, считая, что это будет язык для
ЭВМ четвертого (по-английски — fourth) поколения, однако ему приходилось работать на ЭВМ, ко¬
торая допускала только пять букв названия, этим и объясняется такое необычное имя языка Форт.
В 1971 г. Мура пригласили на работу в Национальную Радиоастрономическую обсерваторию для
разработки программ сбора и обработки данных, получаемых с радиотелескопа. В процессе этой ра¬
боты и появилась первая современная реализация языка Форт (а вторым в мире программистом на
этом языке стала сотрудница Мура Элизабет Ратер), который был принят в качестве основного
языка программирования в Американском астрономическом обществге. К 1973 г. потребность в язы¬
ке стала настолько большой, что Мур и Ратер создали новую фирму Forth, Inc., президентом кото¬
рой стала Э. Ратер. Фирма разработала несколько версий языка Форт для различных марок ЭВМ,
мини- и микроЭВМ, в том числе наиболее современную версию под названием PolyFORTH. По
мнению специалистов фирмы Forth, наиболее важные применения языка Форт в настоящее время
связаны с работой программно-управляемого оборудования в реальном масштабе времени, (хотя
еще в 1974 г. фирма разработала на языке Форт административную систему для управления базой
данных объемом 300 млн. бит информации.) Сейчас фирма Forth сосредоточила свои усилия на об¬
работке изображений, робототехнике и управлении сервоприводами. Недавно появились программы
для таких применений, как автоматизация проверки накопителей на гибких магнитных дисках, для
первого прототипа коммерческого устройства по электрофорезному разделению биологических ма¬
териалов на космическом корабле многоразового использования, для сервосистем роботов, управляе¬
мых голосом, а также для контроля почти всех операций в новом главном аэропорте Саудовской
Аравии.
Однако популярностью язык Форт обязан не только своим авторам Муру и Ратер и фирме Forth.
Базовый язык Форт общедоступен и бесплатно распространяется заинтересованной группой FORTH
Interest Groop (FIG), множество фирм поставляет различные по своим возможностям коммерческие
версии языка, но еще важнее то, что организована группа по стандартизации языка Форт длй того,
чтобы написанные на нем программы могли бы работать на различных компьютерах с минимальны¬
ми затратами на их адаптацию. Группа FIG была создана в конце 70-х гг. активными программи¬
стами и почитателями языка, которые хотели сделать его еще более популярным. Существует мно¬
жество организаций, в том числе несколько отделений этой группы в США и других странах мира,
однако основная деятельность группы направлена на распространение базового диалекта языка,
FIGFORTH (которая реализована на многих мини- и микроЭВМ) и издание журнала FORTH
Dimensions, выходящего раз в два месяца. Содержание журнала показывает, что редакция проявля¬
ет интерес как к модификации и расширению самого языка, так и к решению прикладных задач.
Кроме издания журнала и распространения языка каждый год, начиная с 1980-го, группа FIG созы¬
вает конференцию под названием FORTH Modification Laboratory (FORML), целью которой являет¬
ся встреча пользователей и системных программистов для обсуждения вопросов дальнейшего разви¬
7
тия языка. Кроме этой конференции Институт прикладных исследований фирмы Applied FORTH
Research ежегодно организует в Рочестере (США) конференцию по применению Форта (Rochester
FORTH Application Conference). Институт публикует труды конференции и, кроме того, профессио¬
нальный журнал Journal of FORTH Application and Research, содержащий рефераты, библиографи¬
ческие ссылки на материалы по языку Форт и материалы студенческих исследований.
Группа по стандартизации первоначально возникла в рамках Международного объединения аст¬
рономов. На встрече в Национальной обсерватории Китта (США) в мае 1977 г. был выработан глос¬
сарий языка Форт под шифром AST.01, а после нескольких встреч в Европе, наконец, в феврале
1978 г. в Утрехте (Голландия) был разработан стандарт 1977 г. (FORTH-77), адресованный прежде
всего пользователям микроЭВМ. В октябре 1979 г. встреча на острове Каталина закончилась разра¬
боткой стандарта FORTH-79, который распространяется на ЭВМ всех типов. Осенью 1983 г. состоя¬
лась встреча по разработке стандарта 1983 г, утвержденного в 1984 г, как FORTH-83. Стандарт
Форт-83 отличается от стандарта Форт-79 некоторыми деталями, но не отличается от него по суще¬
ству. Некоторые специалисты, в том числе и поставщики коммерческих версий языка, считают, что
изменения 1983 г. в лучшем случае не привели к совершенствованию языка, в худшем же — вне¬
сли некоторую путаницу, поэтому как Форт-83, так и Форт-79 имеют равное распространение, и
мы рассматриваем здесь обе версии. Несмотря на то, что некоторые изменения могут привести к
смешению обеих версий, были все же некоторые разумные, хотя и не очень важные причины внесе¬
ния этих различий.
Целью стандартизации было создание единого набора слов, чтобы можно было легко переносить
программы с одного компьютера на другой. Стандарт определяет минимальный набор обязатель¬
ных слов и необязательный набор расширяющих слов, например ассемблер и слова из контролируе¬
мого списка, выполняющие точно определенные функции. К сожалению, в стандарте нет никаких
указаний на такие важные компоненты, как числа, символьные строки, на организацию файлов
данных. Концепции группы стандартизаторов частично были приведены в статье ее председателя У.
Рэгсдейла: ”Самой трудной задачей разработки языка является принятие решения о том, что следу¬
ет отбросить. Стандартизаторы сталкиваются с задачей установления равновесия между практиче¬
ски полезными функциями и простейшими функциями, обеспечивающими пользователю возмож¬
ность дополнить программные средства, чтобы решать прикладные задачи”. В результате полу¬
чилось так, что во всех поставляемых версиях Форта возникает необходимость в добавлении собст¬
венных функционально важных слов, что противоречит идее свободной переносимости программ
между различными компьютерами.
Почему же все-таки в Форт не включены слова для выполнения функций, безусловно, предусмот¬
ренных в других языках программирования? Частично это объясняется традиционными применени¬
ями языка, отчасти интересами группы-FIG и многочисленных пользователей языка Форт, в осо¬
бенности любителей, и частично возражениями группы по стандартизации. Во-первых, традицион¬
ной областью применения языка было управление установками в реальном масштабе времени, где
Форт подходит наилучшим образом. Операции со строковыми данными, ввод и вывод чисел, работа
с файлами, связь с операционной системы и другие качества здесь не столь важны, как при реше¬
нии более практических задач, например при проведении инженерных расчетов или в деловой сфе¬
ре. Во-вторых, основной задачей группы FIG было распространение простейшей версии языка
FIGFORTH и, как отмечается в журнале FORTH Dimensions, большинство членов FIG проявляет
интерес скорее к модификациям языка, чем к применению его в повседневной жизни, и поэтому не
имеют побудительного толчка к снабжению Форта такими функциями. Наконец, группа стандарти¬
зации предоставляет пользователю возможность самому выполнить разработку дополнительных ин¬
струментальных средств, сведя к минимуму набор стандартных слов. При этом бремя разработки
возлагается на пользователей и поставщиков промышленных версий языка, лишь в последние три
года некоторые необходимые функции были введены в поставляемых реализациях Форта.
Об этой книге
Итак, мы выяснили, что для нас недостаточно научить вас языку Форт в соответствии со стан¬
дартами 1979 и 1983 г. Мы должны показать вам, как можно расширить язык для ваших конкрет¬
ных задач. Поскольку нам неизвестно, с какой целью вы хотите писать программы для ЭВМ, самое
лучшее, что мы можем сделать, — это привести примеры и изложить некоторые проблемы работы с
очевидными объектами: символьными строками, числами с плавающей запятой, файлами. Чтобы
добиться нашей цели, мы дадим большое количество примеров и упражнений. Упражнения состав¬
8
лены таким образом, чтобы не только помочь в обучении, но и дать исходные тексты программ, ко¬
торые вы сможете использовать в программах. В них представлены основные идеи и приемы, поэто¬
му упражнения нужно непременно просматривать (вместе с ответами в приложении Д) как неотъ¬
емлемую и обязательную часть учебника. Упражнения служат также для того, чтобы побудить вас
к соревнованию, доставить удовлетворение и сделать не таким нудным механическое заучивание,
которое часто требуется при изучении других языков программирования.
Так как оба стандарта языка общеприняты и широко используются, мы должны научить вас обе¬
им версиям. (Данная книга единственная, которая это делает.) Однако большая часть версий Форта
отклоняется от стандартов, поэтому мы должны рассмотреть здесь и некоторые расширения. Мы не
сможем ознакомить вас с дополнениями, введенными во все версии Форта, поэтому мы оказались
перед необходимостью выбора: либо дать примеры из нескольких разных версий, либо углубиться в
конкретную версию Форта. Мы решили, что лучше будет внимательно рассмотреть расширение
конкретной реализации Форта, чем описывать разные версии. Мы выбрали одну из самых расши¬
ренных версий Форта, MMSFORTH, которая поставляется для ЭВМ типа TRS-80 фирмы Radio
Shack и совместимых с IBM PC. Это вовсе не означает, что MMSFORTH — самая лучшая реализа¬
ция (такой, вероятно, вообще не существует), хотя она перекрывает широкий диапазон применений
и мы пользовались ею в своей работе. Если в вашем распоряжении имеется другая версия Форта,
вы сможете из нашего описания MMSFORTH извлечь некоторые полезные идеи относительно рас¬
ширения возможностей вашей версии языка. Чтобы пользоваться этой книгой, вам не нужен
MMSFORTH, мы ссылаемся на него только в примерах и упражнениях, где описывается сам
MMSFORTH.
Наконец, мы задумали эту книгу не только как учебное пособие по языку Форт, но и как спра¬
вочник по языку. Она является справочником как по технике программирования и инструменталь¬
ным программным средствам, так и по словарю и терминологии языка Форт. Имеется очень подроб¬
ный список слов в оглавлении, который позволит вам быстро находить нужные понятия, полный
перечень перекрестных ссылок и подробный перечень содержания и вводимых новых слов перед на¬
чалом каждой главы (с указанием страницы). Глоссарий терминов дает подробное объяснение тер¬
минов и понятий. Наиболее ценным является полный глоссарий слов Форт-79 и Форт-83, некото¬
рых слов из MMSFORTH и наиболее полезных новых слов, введенных в книге. В глоссарии даны
примеры, поясняющие функции слов, а также определения слов, введенных в книге. В глоссарии
описаны также различия между словами Форт-79 и Форт-83, если они имеются. Вы сможете, таким
образом, пользоваться глоссарием не только как справочником, но также как источником идей для
внесения изменений в ваш Форт.
Изучение языка Форт может доставить такое же удовольствие, как решение задач или разгады¬
вание головоломок. Если вы будете относиться к изучению языка, особенно на примерах и задачах,
как к интеллектуальному состязанию, вам не потребуется механическое запоминание, как при изу¬
чении других языков программирования.
Благодарности
Мы благодарим А. Р. Миллера из фирмы Miller Microcomputer Services, JI. Форсли из Института
прикладных исследований по Форту и Дж. Каллахана из фирмы Harward Softworks за очень внима¬
тельный просмотр рукописи и многочисленные предложения и уточнения. Если остались какие-либо
ошибки, то, безусловно, по нашей вине. Мы особенно благодарны А. Р. Миллеру за то, что он по¬
знакомил между собой нас, авторов. Он и его сотрудники Дж. Рибл и Т. Б. Даулинг, ведущий раз¬
работчик MMSFORTH, уделили нам много часов бесценных обсуждений и дали множество полез¬
ных предложений.
9
Глава 1
ЧТО ТАКОЕ ФОРТ?
Язык Форт сильно отличается от других языков программирования для ЭВМ. Но он дает возможность программисту очень
просто общаться с компьютером, и это облегчает его изучение. Форт практически мгновенно реагирует на все, что вы вводи¬
те с клавиатуры, и изучать его лучше всего, непосредственно работая на компьютере. Чаще всего считают его сложным
опытные программисты, которые имеют сложившиеся представления о том, как должен работать язык программирования,
они привыкли, что сначала надо написать длинную программу, прежде чем проверить ее работу. Поэтому постараитесь за¬
быть то, что вы з наете о других языках, садитесь за клавиатуру и давайте начнем.
Учимся на практике
Так же как сложно научиться иностранному языку без собеседника, трудно изучить язык про¬
граммирования без компьютера. Как тот, так и другой язык лучше всего изучается на практике. Вы
можете многое узнать о языке Форт, пользуясь этой книгой, и без компьютера, но хорошо програм¬
мировать на нем вы не сможете. Мы посвятим первую часть этой главы тому, чтобы вы, проэкспе-
риментировав с языком, почувствовали бы себя увереннее. Во второй части мы рассмотрим Форт
более абстрактно, в том числе познакомимся с тем, как он устроен. Итак, попробуем поработать на
Форте.
Очевидно, для начала вам надо включить компьютер и загрузить Форт в машину, но компьюте¬
ров и версий языка так много, что сделать это нужно, пользуясь руководством для вашей машины.
Если вы готовы, напечатайте на клавиатуре
55 111 + <BK>1
Тогда на экране вы увидите ”ок” (все в порядке). Что произошло? Вы ввели числа 55 и 111 и
сложили их, но не увидели результат, потому что не попросили напечатать его. Форт напечатал
”ок”, поскольку он справился с тем, что вы ввели. Сообщение ”ok” — это подсказка-приглашение,
которая говорит вам о том, что Форт готов принимать с клавиатуры новую информацию. Теперь
введите
. <BK>
Слово . (произносится как ’’точка”) просит компьютер выдать число на ^кран. Вы увидите
166 ok
т.е. сумму чисел 55 и 11. Теперь введите
5 6 * <BK>
и получится:
30 ok
Слово * — это символ операции умножения. А теперь попробуйте
55 111 + 2 * . <BK>
На экране должно получиться
332 Ok
Форт сначала произвел сложение, затем было введено чис¬
ло 2, которое было перемножено с суммой, и был напечатан
результат. Теперь введите
60 5 / .
вы увидите
12 ok
Аналогично ’
60 5 - . <BK>
выдаст
55 ok
Попробуйте теперь ввести такую последовательность
1 В качестве клавиши для ввода в Форте используется клавиша возврата каретки, которую мы будем обозначать как <BK>.
При вводе с клавиатуры вы как бы печатаете на машинке, поэтому в тексте слова ’*введите с клавиатуры” и "напечатайте
с клавиатуры” означают одно и то же. — Прим. перев.
10
вы получите
-55 ok
Очевидно, что порядок, в котором вводятся числа, небезразличен. Попробуйте ввести
5 60 / . <BK>
вы получите
0 ok
Что произошло? Форт в минимальном наборе работает только с целыми числами, и, конечно,
5/60 представляет собой дробь, значение которой округляется до 0. Некоторые реализации Форта
могут обрабатывать дроби и числа с плавающей запятой, и мы об этом еще узнаем в гл. 4. А в дан¬
ный момент вам необходимо еще проэкспериментировать с арифметическими действиями, потому
что Форт лучше всего изучать на практике.
Для выполнения арифметических действий вы пользовались стеком языка Форт. Использование
стека — это наиболее существенное отличие языка Форт от других языков программирования.
Представим себе стек как сложенные стопкой числа или колоду карт, помеченных числами. Когда
вводим число, завершая его нажатием клавиши <BK>, мы говорим, что число проталкивается (или
кладется) в стек, как будто бы в колоду добавляется одна карта, а когда производится такая опера¬
ция, как . (т.е. печать числа), мы говорим, что число выталкивается (изымается) из стека, как
будто бы карта вынимается из колоды, в данном случае это делается, чтобы напечатать число. Ког¬
да вы вводите:
55 111 + <BK>
числа 55 и 111 кладутся в стек, а операция + изымает их из стека, складывает и полученную сум¬
му снова кладет в стек.
Наши ’’карты” показаны на рис.1.1.
Пустой стек
Рис.1.1
Мы пользуемся нотацией ”х у +”, которая называется постфиксной (от англ. ”post”- после), по¬
скольку символ операции (оператор) стоит после чисел. Привычная нам алгеб-раическая нотация
”х + у” называется инфиксной. Постфиксную нотацию назыьают также обратной польской нота¬
цией, сокращенно ОПН. Мы расскажем еще о стеке и постфиксной нотации в следующей главе. Од¬
ним из важных достоинств постфиксной нотации является то, что она не нуждается в скобках, при
этом порядок действий определен порядком следования операторов. Попробуйте напечатать на кла¬
виатуре
55 111 + * . <BK>
тогда вы увидите
332 ok
ll
Форт берет из стека два верхних числа 55 и 111, складывает их, кладет сумму назад в стек, поэ¬
тому в нем находятся
2 166
Оператор * затем умножает число 2 на 166, что дает 332, и кладет в стек результат, который по¬
сле этого печатается. Наша аналогия с картами показана на рис.1.2. Использование обратной поль¬
ской нотации в выражении
2 55 111 + *
эквивалентно
2 * (55 + 111)
в обычной алгебраической нотации. Если вы набрали на клавиатуре
55 111 2 + *
то это эквивалентно вводу
55 * (111 + 2)
что, конечно же, отличается от предыдущего примера.
Пустой стек
Пустой стек
Рис. 1.2
Поначалу ОПН и стек немного смущают, но после небольшой практики вы будете считать ее по
меньшей мере такой же естественной, как и обычная алгебраическая нотация. В следующей главе у
вас будет очень много практики, но вам уже сейчас хочется сделать что-нибудь самостоятельно. Да¬
вайте пока отложим в сторону детали, связанные со стеком и обратной польской записью, переста¬
нем использовать Форт как калькулятор и попробуем написать программу. Наберите следующий
текст:
: TIMES2 * . ; <BK>
и вы увидите только ”ок”, т.е. будто бы ничего не произошло.
12
Но теперь, если вы напечатаете
5 TIMES2 <BK>
то увидите
10 ok
Вы только что определили (описали) слово TIMES2 (умножить_на_2) на языке Форт, которое пред¬
ставляет собой крошечную программу: ее действие состоит в том, чтобы поместить в стек число 2,
умножить его на то значение, которое было в стеке раньше, и затем напечатать результат. : Сдвое-
точие) дало Форту указание начать определение и принять слово TIMES2 (каждая следующая пор¬
ция ввода заканчивается пробелом) в качестве имени нового слова Форта. Три символа (2, * и .) —
ранее существовавшие слова Форта, которые рассказывают программе, что она должна делать, за¬
тем ; (точка с запятой) указывает на то, что определение закончено. (Обратите внимание, что в ка¬
честве слова в Форте может выступать один символ, как, например, в английском языке слова
”1”(я) и ”а” (артикль).)
Слова Форта можно использовать двояко: либо как команды, которые должны быть немедленно
исполнены, примерами этого были наши арифметические упражнения, либо для описания новых
слов. Заметим, что ввод
5 TIMES2
приводит к такому же результату, как и ввод
5 2 *
Ввод слов с клавиатуры обычно приводит к такому же эффекту, как и ввод их в определения, хо¬
тя имеются и очень важные и необычные исключения.
Почти все, что вы приказываете Форту сделать, обозначается словом или числом. Учтите, что
между словами и числами должен быть по крайней мере один пробел. Операторы типа + , * , / и .
— это слова, и даже : и ; — это тоже слова, которые указывают начало и конец описания слова.
(Как вы уже заметили, если в тексте встречается Форт-слово, мы выделяем его полужирным шриф¬
том и знаки пунктуации отделяем от него пробелами.)
Программа на языке Форт пишется путем составления описаний (определений) новых слов, для
которых используются ранее определенные слова, пока не будет определено главное слово, т.е. то
слово, которое нужно ввести, чтобы исполнить главную программу. Большинство слов языка созда¬
ется в виде так называемых определений через двоеточие, потому что их определение начинается с
: и заканчивается ; (точкой с запятой).
Программы на языке Форт фактически являются расширением самого языка за счет включе¬
ния в него новых слов и операторов.
Когда слово, например TIMES2, описывается в форме определения через двоеточие, то говорят,
что оно компилируется (помещается, заносится) в словарь Форта, который и на самом деле пред¬
ставляет собой словарь, т.е. комплект слов, описанных на языке, который может понимать компью¬
тер. Когда какое-либо слово выполняет свои действия, как, например, когда вы вводите
5 TIMES2 <BK>
мы говорим, что слово исполняется. Компиляция и исполнение — это две основные задачи, кото¬
рые выполняет Форт. Если вы знакомы с другими языками программирования, то знаете, что они
компилируют или исполняют всю программу целиком, но не такие мелкие кусочки, как описания
слов. Одной из причин того, что Форт так легко взаимодействует с пользователем, является как раз
то, что программа на нем может быть легко написана (и скомпилирована) в виде набора небольших
фрагментов, каждый из которых может быть опробован (и исполнен) немедленно. Именно поэтому
Форт так легко изучать экспериментально.
Хотя TIMES2 в буквальном смысле представляет собой настоящую программу, она настолько не¬
затейлива, что вы вряд ли написали бы что-нибудь подобное. Давайте сделаем немного более слож¬
ную программу. Сможете ли вы догадаться, что делает следующая программа
: SQUARE DUP * ;
Слово DUP делает копию (дубликат) числа, находящегося в стеке. Предположим, что мы ввели
6 SQUARE . <BK>
результатом этого является
36 ok
Слово SQUARE делает копию числа 6 в стеке, оставляя 6 6, а затем перемножает эти два одина¬
ковых числа, получая 36. Очевидно, слово . (точка) выводит результат. Теперь мы можем опреде¬
лить
• CUBE DUP SQUARE * ;
13
для вычисления куба числа. Если вы знакомы с другими языками программирования, то заметите,
что наши слова похожи на подпрограммы, и, возможно, запротестуете против написания таких ко¬
ротких подпрограмм. Например, вы бы предпочли определить слово CUBE иначе :
: CUBE DUP DUP * * ;
но на языке Форт дополнительные затраты времени исполнения и компьютерной памяти будут
незначительны, если вы поделите длинные определения на более короткие (это называется делени¬
ем программы на части), такие программы проще будет понимать и переделывать. Определения
нужно стремиться делать короткими. Приведем полный листинг программы :
: SQUARE ( n1 — n2: квадрат числа ) DUP * ;
: CUBE (n1 — n2: куб числа) DUP SQUARE * ;
Мы добавили комментарии, чтобы программа стала более понятной. Любые слова в Форте, за¬
ключенные в круглые скобки (с обязательным пробелом после открывающей скобки), игнорируют¬
ся, в них заключаются комментарии для программиста. В начале комментария в соответствии с
принятым для Форта соглашением помещается информация о состоянии стека в виде nl — n2. Ком¬
ментарий о стеке показывает, что было в стеке до исполнения и что стало после исполнения дан¬
ного слова. Так, например, комментарий о стеке для * выглядит так: ( nl n2 — n3). Комментарий о
стеке необходим для всех, кроме самых коротких слов. Без него очень легко забыть, как работает
программа.
В следующем разделе мы напишем нетривиальную программу, которая будет строить на экране
столбиковую диаграмму, или так называемую гистограмму, но сначала вам нужно, чтобы идеи,
приведенные в этом разделе, усвоились, а для этого проделайте несколько упражнений.
Упражнения
Упражнения, которые будут приведены в этой книге, не являются дополнительным приложением к тексту. Они составля¬
ют его неотъемлемую часть и должны стать неотъемлемой частью процесса освоения языка. Упражнения составлены так,
чтобы служить четырем целям : 1) тренировке, 2) углублению понимания принципов, изложенных в этой книге, 3) разви¬
тию техники программирования, 4) иногда определению слов широкого практического применения. Вы должны попытаться
проделать все упражнения, но, если у вас возникнут затруднения, не стесняйтесь заглянуть в ответы приложения Д : некото¬
рые задачи бросают вам вызов. Упражнения также помогут вам продвигаться в изучении языка. Мы предлагаем серию уп¬
ражнений после часа-двух интенсивного изучения текста. Наконец, попробуйте сами придумать собственные упражнения,
связанные с вашими личными интересами. Один из лучших способов изучения какого-либо языка программирования — пи¬
сать собственные упражнения и программы.
1. Определите в уме, что будет выведено на экран следующими операциями:
а) 20 2 / . б) 2 20 / .
в) 1 2 3 * * . г) 3 2 1 + / .
д) 100 5 DUP * / .
2. Преобразуйте следующие выражения в обратную постфиксную форму:
а) 1 * 2 б) 1 * 2 / 2
в) 1 + 2 / 3 г) (1 + 2) / 3
д) 3 / (2 + 1) e) (3 + 4) / (2 + 1)
3. Опишите слово POWER4 для возведения числа в четвертую степень, используя слово CUBE.
4. Опишите слово NEWPOWER4 (возведение_в_четвертую_степень) используя словб SQUARE вместо CUBE.
5. Согласно теореме Пифагора квадрат гипотенузы прямоугольного треугольника равен сумме квадратов прилежащих
сторон. Определите слово PYTHAGORUS для определения квадрата гипотенузы по заданным в стеке длинам его сторон. Вам
может потребоваться слово SWAP (переставить), которое переставляет между собой два числа, находящиеся на верху стека.
Так, например,
4 5 SWAP <BK>
оставит два верхних числа:
5 4
6. Определите слово AREA (площадь), которое должно вычислять площадь круга, помноженную на 100, если задан ра¬
диус. Используйте число 314 вместо значения числа Пи, помноженного на 100.
7. Используя слово AREA, определите слово VOLUME (объем) для вычисления объема цилиндра, помноженного на 100.
В стеке должны лежать значения радиуса — на вершине и значение высоты цилиндра вторым сверху. Переделайте
VOLUME в XVOLUME, которое ожидает данные из стека в обратном порядке ( высота цилиндра — на вершине, радиус —
вторым сверху). Какая программа имеет большую эффективность ( с точки зрения быстродействия) ?
14
Полезная программа
Хотя из соображений удобства мы знакомим вас с языком Форт на примере арифметических опе¬
раций, нужно подчеркнуть, что компьютеры приносят большую пользу не только в математике.
Например, рукопись этой книги была подготовлена с помощью программы обработки текстов, напи¬
санной на языке Форт. Одно из цаиболее полезных применений компьютера состоит в преобразова¬
нии огромного количества данных в такую форму, которая легче воспринимается человеком. В час¬
тности один из лучших способов — представление данных в графической форме. Несмотря на то,
что многие языки программирования (включая некоторые версии Форта) имеют множество слож¬
ных графических команд, одним из наиболее распространенных способов представления графиче¬
ских данных является ’’быстрый и грубый” график, построенный из прямых линий или столбиков.
Возможно, вы привыкли к представлению столбиков в виде сплошных вертикальных прямоугольни¬
ков, однако неплохо выглядят также столбики, построенные из рядом стоящих букв, которые печа¬
таются по горизонтали. Например,
xxxxxxxxxx
xxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxx
xxxxxxx
представляет собой вполне наглядную гистограмму. Мы проследим весь процесс составления про¬
граммы для построения подобной гистограммы, а затем рассмотрим ее с точки зрения структуры
Форта, после чего вы сможете модифицировать программу в следующей серии упражнений.
В самом начале определим слово
: TASK ;
TASK — это слово, которое ровным счетом ничего не делает, кроме того, что помечает позицию
в словаре. Но если ввести
FORGET TASK <BK>
то слово TASK будет удалено из словаря вместе со всеми теми словами, которые были определе¬
ны после слова TASK. Поэтому, если вы сделали ошибку в программе и хотите.начать ее сначала,
достаточно ввести
FORGET TASK <BK>
чтобы снова оказаться в том месте, с которого вы начали. Считается хорошей манерой начинать
программу с подобного, не имеющего другого смысла слова, обычно для этого используется именно
слово TASK.
Теперь нам нужно описать слово, которое будет печатать на экране строку литер ”Х” или ’’стол¬
бик”. Мы будем традиционно считать, что ширина экрана равна 64 позициям, таким образом, пре¬
делы длины столбика от 1 до 64. Прежде всего нам нужно узнать, как напечатать один символ ”Х”.
Самый простой способ — это использовать слово .” . Вначале определим слово, которое должно пе¬
чатать ”Х” один раз, т.е.
.X ." X” ;
(В данном случае точкой в названии .X мы отмечаем, что это слово должно что-то напечатать,
это общепринятое соглашение
на языке Форт.) Теперь определим следующее слово :
: TEST 40 0 DO .X LOOP ;
и, когда вы введете TEST <BK>, вы увидите на экране :
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ok
т.е. 40 букв ”Х”, после которых выведено подтверждение.
Слова DO и LOOP, предписывают Форту повторить исполнение слов, находящихся между ними,
причем число повторений определяется двумя числами, находящимися в стеке, когда встречается
слово DO, в данном случае 40 раз. Зацикливание программы, или многократное исполнение набора
инструкций, очень важная возможность языка Форт, как и других компьютерных языков.
Но слово TEST — это еще не то, что нам нужно : оно печатает столбик из 40 литер ”Х”, а нам
необходимо печатать столбики различной длины, в зависимости от числа, находящегося на вершине
стека. Чтобы сделать это, нужно просто вынести из определения число, задающее верхний предел
цикла DO-LOOP, т.е. мы можем определить слово
: BAR ' 0 DO .X LOOP CR ;
Так как для слова DO необходимо иметь в стеке два числа, то для исполнения слова BAR (стол¬
бик) вам нужно задавать одно из них — предел цикла. Если вы введете 23 BAR, то на экране
15
увидите
xxxxxxxxxxxxxxxxxxxxxxx
ok
Обратите внимание, что слово ”ок” печатается на следующей строке. Это результат действия сло¬
ва CR, которое производит перевод строки и возврат каретки. Без слова CR все наши столбики бу¬
дут выстроены в ряд друг за другом, а не один под другим.
Теперь нам нужно напечатать несколько столбиков различной длины. Другими словами, мы хо¬
тим повторить программу BAR несколько раз. Для этого нам потребуется еще один цикл DO. Опре¬
делим слово
: TESTGRAPH CR 4 0 DO BAR LOOP ; <BK>
и теперь введем строчку
5 10 15 20 TESTGRAPH <BK>
Вы увидите гистограмму
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxx
xxxxx
Конечно, данное определение будет исполняться только с четырьмя числами, поскольку в
TESTGRAPH задано только четыре повторения цикла.
Мы уже почти пришли к нашей программе построения гистограммы, но в нее нужно ввести еще
несколько усовершенствований. Во-первых, мы предполагали, что ширина экрана равна 64 позици¬
ям, поэтому число больше 64 недопустимо. Нам нужно как-то ограничить длину столбика. Во-вто-
рых, Форт должен каким-либо образом определить, сколько чисел находится в стеке и, следователь¬
но, сколько нужно построить столбиков. Поэтому число 4 в цикле 4 0 DO в программе
TESTGRAPH нам не нужно. Будем вводить усовершенствования в программу по одному. Во-пер¬
вых, как можно ограничить длину столбика? Вот программа, которая может выполнить это :
: LIMITBAR DUP 64 > IF DR0P 64 THEN BAR ;
Рассмотрим, как она работает. Она делает копию числа, находящегося на вершине стека, выраже¬
ние 64 > сравнивает это число с числом 64, и если оно больше 64, то в стек возвращается значение,
которое оператор IF интерпретирует как истину. Если оператор IF встречает значение истина, то
исполняется та часть программы, которая находится между словами IF и THEN, в данном случае
DROP 64 , т.е. убрать из стека число, которое там находится, и положить вместо него число 64. Ес¬
ли число в стеке меньше или равно 64, то выражение 64 > возвращает в стек значение ложь, кото¬
рое оператор IF воспринимает как указание перейти на слово, следующее после THEN, в данном
случае BAR. Если число больше 64, т.е. исполняются слова между IF и THEN, то затем процесс
продолжается также после слова THEN, но слово BAR печатает 64 литеры ”Х”, т.е. столбик, зани¬
мающий всю ширину экрана.
Этот пример требует некоторого осмысления, но главное, что мы хотели бы в нем подчеркнуть,
— это большая важность в языке Форт конструкции IF...THEN. Она позволяет программе разветв¬
ляться, т.е. производить выбор одной из двух возможностей, как, например, в данном случае: печа¬
тать или не печатать столбик из 64 символов ”Х”. Есть и другие способы разветвления программы,
но данная конструкция является основной. Ветвление — это важное свойство любого языка про¬
граммирования, поскольку оно обеспечивает гибкость и удобство программирования задач любой
сложности. Если вы знакомы с другими языками программирования, то, вероятно, обнаружите, что
конструкция языка IF...THEN рассматривает слова DUP и BAR как подпрограммы. Подпрограммы
(т.е. небольшие программы, находящиеся внутри больших программ) всегда определяются в Форте
самостоятельными словами в том смысле, что Форт-программа построена из большого числа корот¬
ких подпрограмм.
Второе улучшенце состоит в том, чтобы Форт сам определял, сколько чисел нужно отобразить на
гистограмме. Это делается с помощью слова DEPTH (глубина), которое возвращает в стек количе¬
ство чисел в стеке, и не разрушает стек. Например, если ввести
2 8 9 4 3 DEPTH . <BK>
вы увидите
5 ok
на экране (а сами числа по-прежнему находятся в стеке). Вот
теперь мы можем определить слово GRAPH, которое будет строить гистограмму чисел, находящих¬
ся в стеке:
16
GRAPH CR DEPTH 0 DO LIMlTBAR LOOP ;
Теперь посмотрим на всю программу, собранную вместе и с добавленными комментариями :
TASK ; ( Слово, которое должно забываться при стирании программы)
X . ’’ X” ; ( Символ для печати)
BAR ( n — ) 0 DO X LOOP CR; (Вывод столбика из X)
LIMITBAR ( n — ) DUP 64 > IF DROP 64 THEN BAR ,
( Строит столбик из не более 64 символов )
GRAPH ( n1 n2 n3 —) CR DEPTH 0 DO LIMITBAR LOOP,
Чтобы запустить программу GRAPH, вы должны ввести несколько чисел и после этого — слово
GRAPH. Итак, считая, что вначале стек был пустым, после ввода
9 10 12 14 10 6 GRAPH
получаем гистограмму:
ххххххххх
xxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxx
xxxxxx
Несмотря на свою краткость, эта программа демонстрирует несколько важных свойств языка
Форт. Во-первых, хотя числа в Форте могут храниться в виде констант и переменных, как и в дру¬
гих языках программирования (см. гл. 6), чаще всего это не требуется. Обычно достаточно исполь¬
зования стека.
Во-вторых, Форт-программа состоит из последовательности оп-ределений слов через предшеству¬
ющие им слова, так, напри-мер, для слова GRAPH используется определение слова LIMITBAR, ко¬
торое основано на определении слова BAR, использующем определение слова .X , в свою очередь,
включающее определение слова .”, а последнее является первичным словом любой версии языка
Форт. Таким образом, для определения слов применяются ранее данные определения, основанные
на первоначально определенных словах языка. В третьих, Форт-программы компактны. Текст про¬
граммы на Бейсике или Фортране, предназначенной для решения этой задачи, будет намного длин¬
нее. Форт-программы обладают большим быстродействием, хотя пока данное утверждение надо
принять на веру. В четвертых, Форт-программа составлена из коротких слов или определений, каж¬
дое из которых может быть проверено и отлажено отдельно. Форт-программу сравнительно легко
усовершенствовать и изменить без изменения большинства входящих в нее частей. Например, про¬
грамма GRAPH может быть переделана для экрана, имеющего ширину 80 позиций, простой заме¬
ной числа 64 на 80 в слове LIMITBAR. Наконец, Форт-программу, как правило, проще понять, чем
любую другую, просмотрев определение последнего слова. Так, например, глядя на определение
слова GRAPH, вы увидите, что нужно понять определение слова LIMITBAR, которое в свою оче¬
редь, приводит к слову BAR, а оно уже почти очевидно само по себе. Программы легко читаются
(особенно если они снабжены продуманными комментариями и удачно выбраны имена слов), прав¬
да, не сверху вниз, как читаются программы на Бейсике, Фортране или Паскале. Форт-программу
прочитывают, начиная с какого-либо важного слова, разбирая затем, для чего предназначено каж¬
дое слово, входящее в его определение.
В заключение следует сказать несколько слов о методике программирования на языке Форт.
Прежде всего вы должны хорошо понять, что будет делать программа, а отсюда вы придете к сло¬
вам, которые для нее необходимо определить. Например, для программы GRAPH, очевидно, необ¬
ходимо слово BAR, а для него, в свою очередь, потребуется слово .X. Разработка Форт-программы
продвигается одновременно на двух уровнях : на системном уровне, когда на основе понимания ко¬
нечной цели определяются слова, которые нужно описать, и на уровне подпрограмм, иначе говоря,
слов, когда описывается и проверяется каждое слово. Программирование на языке Форт — это орга¬
нический интуитивный и творческий процесс, который завершается, как правило, эффективными
программами как на уровне большой системы, так и на уровне отдельных слов (подпрограмм).
Упражнения
Мы собираемся предложить вам модифицировать нашу графическую программу. Проще всего заниматься этим, если вы
запишете ее на диск, а затем отредактируете, чтобы внести изменения по условиям упражнения. Форт-система обычно со¬
храняет программы на дискетах в нумерованных блоках емкостью 1024 символа в каждом, что соответствует размеру экрана
17
(16 строк по 64 символа). Поскольку разные версии Форта отличаются способами хранения и редактирования блоков на ди¬
ске, рекомендуем обратиться к документации на вашу Форт-систему, чтобы узнать, как сохранить программу на диске. Для
того чтобы ввести копию программы на диск, вы будете набирать ее на клавиатуре и пользоваться редактором. Затем про¬
грамму можно будет загрузить, если ввести номер блока и после него cnoBo LOAD (загрузить), например
68 LOAD
Эта команда вызовет интерпретацию слов, которые с помощью редактора были записаны на диск, так же, как если бы их
вводили с клавиатуры. Компьютер не знает и ему безразлично, откуда производится ввод, с клавиатуры или из блока на дис¬
ке. Большое преимущество этого состоит в том, что можно модифицировать или редактировать программу на диске, не вводя
ее каждый раз целиком с клавиатуры. Старую скомпилированную программу можно забыть (т.е. удалить ее из памяти
ЭВМ), если ввести FORGET TASK перед вводом измененной программы словом LOAD. Если вы пока не хотите обременять
себя изучением редактора, можете каждый раз вводить программу с клавиатуры. Но нельзя просто ввести скорректированное
определение какого-либо слова с клавиатуры, если оно входило в определение какого-либо последующего слова. Дело в том,
что это последующее слово было скомпилировано раньше совместно со старым определением скорректированного вами слова.
В гл.12 мы подробно обсудим разные редакторы и ввиду того, что многие редакторы, поставляемые с Форт-системами, доста¬
точно примитивны, мы предложим вам редактор, который, возможно, понравится больше. Однако вам все же придется нау¬
читься пользоваться редактором вашей Форт-системы хотя бы только для того, чтобы ввести редактор, описанный в гл. 12.
1. Введите целиком программу с диска, если у вас есть такая возможность, и забудьте ее. Теперь снова введите. В чем
преимущества ввода с диска?
2. Модифицируйте программу GRAPH так, чтобы вместо "X” она печатала столбики черточками (измените только один
символ).
3. Модифицируйте программу так, чтобы гистограмму можно было рисовать на экране шириной 40 позиций. Снова изме¬
ните программу на 80 позиций.
4. Измените программу так, чтобы в нее можно было вводить числа от 0 до 6400. Для этого в подходящем месте програм¬
мы LIMITBAR нужно помесТйть операцию 100 /. Назовите эту программу LIMITBAR1.
5. Переделайте LIMITBAR в LIMITBAR2 так, чтобы она могла принимать числа от 0 до 10000, причем числу 10000 дол¬
жен соответствовать столбик длиной 64 символа.
6. Измените программу GRAPH, чтобы она строила гистограмму не более 16 чисел. Вам потребуется оперативно при¬
менить конструкцию IF...THEN.
7. Измените слово BAR так, чтобы в конце столбика, которым представляется число, печаталось это число. Вам потребу¬
ется вставить слова DUP и . в подходящих местах определения BAR. Теперь измените LIMITBAR в LIMITBAR3 так, чтобы
максимальное число литер "X" было равно 50. Мы надеемся, что эти упражнения доказали вам, почему определение
программы через отдельные работы, задачи и слова позволяет особенно легко изменять ее при необходимости.
Форт ... Почему он такой необычный?
В оставшейся части этой главы мы хотели бы познакомить вас с тем, что такое язык ЭВМ и с не¬
которыми абстрактными, теоретическими и концептуальными особенностями языка Форт. Если да¬
же вы знакомы с компьютерами и языками программирования, вам следует прочитать это, чтобы
узнать о некоторых уникальных свойствах языка Форт.
Что такое машинный язык?
Чтобы задать вопрос, почему Форт работает не так, как другие языки программирования, надо
сначала спросить, а что такое язык ЭВМ? Компьютер — это машина, которая может производить
включение и выключение переключающих устройств с очень большой скоростью (миллион или бо¬
лее раз в секунду). Каждое переключающее устройство может представлять ”1” (включено) или
”0”(выключено), большая часть этих переключающих устройств находится в памяти ЭВМ. Компь¬
ютер может хранить числа, буквы и другие данные в памяти, поскольку он обладает способностью
переводить их в последовательность из единиц и нулей (включенных и выключенных состояний пе¬
реключающих устройств). Так, например, буква R обычно хранится в ЭВМ в виде 01010010, буква
S — в виде 0101011, а буква г — как 01110010. Но еще важнее, что и инструкции, которые указы¬
вают компьютеру, что он должен делать, также хранятся в памяти в виде последовательности из
единиц и нулей.
Центральное процессорное устройство (ЦПУ) считывает эти последовательности, определяя,
что делать. Эта последовательность единиц и нулей, на которую отзывается центральный процес¬
сор, представляет собой программу самого низкого уровня, и фактически только такую программу
процессор может непосредственно исполнять. В отличие от компьютеров человек не обладает спо¬
18
собностью мыслить категориями единиц и нулей. Поэтому он нуждается в языке для общения с
компьютером. Самый простой язык, который называется машинным языком, представляет собой
попросту процессорные инструкции в виде последовательностей из многоразрядных чисел, которые
хранятся в компьютере представленными в виде единиц и нулей. Однако и такое представление
трудно для человеческого восприятия, поэтому пользуются языком более высокого уровня, в кото¬
ром каждая инструкция представляется некоторой аббревиатурой (или, как говорят, мнемоникой),
которая, к примеру может указывать, что компьютер должен переслать число из памяти в регистр
ЦПУ. Каждая мнемоническая инструкция, в свою очередь, ассемблируется (размещается) с по¬
мощью программы, которая написана pa машинном языке так, чтобы сформировать в памяти по¬
следовательность из единиц и нулей. Программа, предназначенная для этой цели, называется ас¬
семблером, а язык этого уровня также называется ассемблером, или языком ассемблера.
Язык ассемблера обладает тем достоинством, что он может управлять всеми доступными данному
процессору операциями и, кроме того, создает очень компактные программы, которые эффективно
используют запоминающее устройство и обеспечивают максимально возможную скорость работы
ЭВМ. Недостатком же его является то, что программист должен описать действия ЭВМ вплоть до
самых мелких деталей. Например, нельзя на языке ассемблера дать команду компьютеру перемно¬
жить два числа, необходимо расчленить процесс умножения на ряд более простых шагов. Програм¬
ма умножения двух чисел, записанная на языке ассемблера, может вылиться в некоторое количест¬
во строчек. Поэтому на языке ассемблера обычно пишут те программы, которые должны работать с
максимально возможным быстродействием с учетом мельчайших подро^бностей работы компьютера,
например программы для управления запоминающим устройством на магнитных дисках.
Язык ассемблера все же неудобен для решения большинства практических задач, поэтому в основ¬
ном его используют для написания других языков программирования, чтобы еще на одну ступень
подняться над машинным языком ЦПУ. Языки такого рода называют языками программирования
высокого уровня. К ним относятся Фортран, Бейсик, Кобол, АПЛ, Паскаль и тот, который нас здесь
больше всего интересует — Форт. Они преобразуют понятные человеку символы (например, * —
обозначение операции умножения двух чисел) в последовательность понятных компьютеру инст¬
рукций из единиц и нулей. Следовательно, языки высокого уровня выполняют роль переводчиков
между человеком и ЭВМ.
Языки высокого уровня традиционно разделяются на два класса : интерпретирующие и компи¬
лирующие. Компилирующим языком называют такой.язык, который целиком пресйбразует исходную
программу в машинный язык, исполняющуюся так же,/как программа,написанная непосредственно
на машинном языке. Текст программы, написанный* на* языке близком к обычному английскому,
называется исходным кодом, инструкции на машинном языке — скомпилированным кодом. Часто
текст программы называют просто кодом, будь то* язык машины, ассемблера или язык высокого
уровня. Компилирующие языки появились первыми,'са#пя$и старыми среди них являются Фортран,
Алгол и Кобол, которые еще и сейчас применяются преимущественно на больших ЭВМ. Достоинст¬
во компилирующих языков состоит в том, что громаДннй исходный код программы не нужно раз¬
мещать в машине, она транслируется в машинный код один раз, а исполняемая программа работает
обычно очень быстро и для ее размещения требуется меньше места в памяти. Недостаток же состо¬
ит в том, что сам процесс компиляции очень трудоемок, трудоемок также процесс внесения исправ¬
лений и изменений в программу (отладки программы), потому что при внесении любого изменения
ее приходится заново компилировать целиком.
Интерпретирующий язык транслирует исходный код программы (интерпретирует) строчку за
строчкой при каждом исполнении программы. Интерпретирующим языком является Бейсик, хотя
также существуют и компилирующие версии этого языка. Очевидно что при исполнении программы
на интерпретацию расходуется время, поэтому интерпретирующие языки по своей природе работа¬
ют медленнее, чем компилирующие. С другой стороны, исходный текст программы можно легко из¬
менить, потому что он всегда находится в компьютере, а скорректированную программу можно бы¬
стро и просто проверить. Обычно интерпретирующие языки поощряют при их изучении и про¬
граммировании к применению метода проб и ошибок. Кстати, язык Бейсик был первоначально раз¬
работан в Дартмутском колледже как раз для изучения компьютерных языков. Интерпретирующие
языки обычно общительны, в том смысле, что они делают сбщение между программистом и про¬
граммой (и, следовательно, компьютером) относительно несложным.
19
Что такое Форт ?
Из всего сказанного может показаться, что у нас есть только два выбора среди языков програм-
мированйя: либо быстрые, но громоздкие компиляторы, либо медленные, но зато общительные ин¬
терпретаторы. К счастью, Форт является еще одной альтернативой. Программы, написанные на
языке Форт, по быстродействию не уступают, а то и превосходят программы, написанные на ком¬
пилирующих языках. Форт-программы очень легко изменяются и отлаживаются. Еще одним их до¬
стоинством является то, что они обычно занимают в памяти меньше места, чем программы на дру¬
гих, как компилирующих, так и интерпретирующих языках. Дополнительное преимущество языка
заключается в том, что он дает возможность программисту определить некоторые слова на языке
ассемблера, когда требуется максимальное быстродействие, т.е. в нем совмещаются преимущества
языка высокого уровня и языка ассемблера. Чем объясняются все эти достоинства языка Форт?
Тем, что он является интерпретирующим языком с шитым кодом. Чтобы разобраться, что это зна¬
чит, нужно рассмотреть, что происходит при определении Форт-слова. В поставляемом потребите¬
лям виде базовая Форт-система содержит множество слов, которые были определены не с помощью
других слов, а непосредственно на машинном языке (они составляют часть, называемую ядром язы¬
ка). Эти слова используются для описания других слов, и их называют примитивами. Примерами
примитивов являются слова *, +, DUP, SWAP и т.д. Если вы компилируете слово SQUARE, вводя
: SQUARE DUP * ; <BK>
в компьютер не вводится никакого нового машинного кода, вы только предлагаете ему соединить
вместе коды слова DUP и * под именем SQUARE. Точно также и CUBE :
: CUBE DUP SQUARE * ;
не вводит нового машинного кода. Аналогично и слово POWER4, которое вы определили в одном из
упражнений. Компиляция состоит как бы в протаскивании одной нити через различные определе¬
ния с исполнением встречающихся по пути примитивов и машинных команд. Когда исполняется
слово POWER4, ”нить” проходит так, как показано на рис.1.3. Все необходимые действия, кроме
ввода в память, были выполнены с помощью двух примитивов DUP и *, входящих в ядро и опреде¬
ленных на машинном коде. Если вы знакомы с компиляцией в других языках программирования,
то заметите, что Форт осуществляет ее совершенно по-другому. В гл.14 и 15 мы подробно рас¬
смотрим, как работает компилятор шитого кода. В книге Лулигера (1981) рассматриваются некото¬
рые теоретические вопросы разработки интерпретирующих языков, использующих шитый код. Те¬
перь мы можем понять причину многих достоинств языка Форт: программирование в диалоговом
режиме, гибкость, большая скорость, минимальные потребности памяти и такое же полное управле¬
ние оборудованием компьютера, какое доступно ассемблеру или машинному языку. Вы уже убеди¬
лись в больших возможностях общения с языком на примерах и упражнениях этой главы. Большое
преимущество в скорости работы перед другими интерактивными языками объясняется тем, что по¬
следние, как правило, интерпретирующие.
POWER4 CUBE SQUARE
DUP
“ DUP^
Рис. 1.3
Поскольку интерпретация включает в себя преобразование кода программы в машинный код строчка за строчкой во время
исполнения программы, то и Форт, и любой другой компилирующий язык должны работать намного быстрее. Скорость toX
работы складывается из нескольких компонент. Во-первых, использование стека позволяет сэкономить время на извлечение
переменных из памяти, если выполняются операции с числами. Во-вторых, переход от примитива к примитиву и от слова к
слону также занимает очень мало времени. B-третьих, в процессе исполнения программы Форт делает только минимальную
20
проверку ошибок, поэтому не подключаются никакие *’скрытые” программы для их обнаружения, например, если вы пытае¬
тесь разделить число на 0. Проверка ошибок целиком возлагается на программиста. Поскольку Форт-программа составляется
и отлаживается слово за словом, это может при отладке сильно мешать, поэтому проверку ошибок можно предусматривать
”по обстоятельствам”. Наконец, программируя на Форте, приходится отчетливо представлять себе и учитывать, что и как де¬
лает компьютер, поэтому программа получается более эффективной.
Очень малая потребность в памяти, присущая Форту, вызывается применением шитого кода. В отличие от других ин¬
терпретирующих языков не требуется, чтобы код исходной программы всегда находился в памяти. И в отличие от любого
компилирующего языка каждый примитив и процедуры, определенные на машинном языке, хранятся в памяти в единствен¬
ном экземпляре. Большинство же компиляторов добавляют к программе модули машинного кода при каждом обращении к
этому модулю. Когда компилятор встречает в исходном коде вызов функции, он отыскивает в библиотеке определенные на
машинном коде процедуры и размещает их в памяти, поэтому, если функция вызывается несколько раз, то в память будет
помещено несколько копий процедуры. Интерпретирующий язык с шитым кодом записывает процедуру в память только
один раз и делает переход к ней из любого места, где она потребуется. Поэтому Форт-программа может занимать в памяти
значительно меньше места, чем эквивалентная программа, написанная, скажем, на Фортране.
Наконец, Форт обладает потрясающей способностью управлять компьютером, поскольку на нем довольно легко опре¬
делить новые примитивы, т.е. процедуры в машинных кодах. Если определение слова начинается с
CODE, то все, что следует после этого слова, описывается на языке ассемблера и при ассемблировании превращается в
машинный код, который становится частью определения этого слова. Описание вы найдете в гл. 16. Поскольку слова Форта
можно легко комбинировать с мнемоникой ассемблера, он обладает мощью ассемблера и удобством языка высокого уровня.
Очевидно, что программирование на Форт-ассемблере не является обязательным, но приятно сознавать, что такая возмож¬
ность имеется при необходимости.
Некоторая критика языка Форт
Оппонентами языка Форт чаще всего бывают программисты, не сумевшие понять его общие идеи и большие возможно¬
сти. Форт устроен так, чтобы обеспечить максимальную мощь и гибкость языка высокого уровня. Это подразумевает, что
функции, которые заранее определены в других языках, в минимальных реализациях языка Форт могут отсутствовать. На¬
пример, стандарт языка Форт описывает только простейшие методы обращения с символьными строками, не содержит про¬
цедур для работы с матрицами и векторами, не обеспечивает чтение данных из файлов, простейшие реализации не могут об¬
ращаться с числами с плавающей запятой (как, например, число 293.45, которое имеет целую и дробную части). Кроме то¬
го, считается, что Форт-программу трудно читать и понимать, потому что слова в определениях могут быть названы коротки¬
ми и загадочными именами.
Ни одно из этих критических замечаний в действительности не обосновано, поскольку они относятся к конкретным
реализациям и версиям Форта или к программистам, работающим на этом языке. Поскольку Форт допускает определение
слов на языке ассемблера, буквально все, что можно запрограммировать на других языках, может быть сделано и на Форте.
Можно даже написать на языке Форт другие языки программирования. А так как на языке Форт легко определяются новые
слова, то нетрудно написать процедуры либо на языке ассемблера, либо с помощью других слов Форта, которые отсутствуют
в стандарте, т.е., например, слова для работы с символьными строками или матрицами. Работая на языке Форт, вы можете
сами определить новые слова, которые нужны для вашей конкретной задачи. Например, если вы однажды определите слово
для вычисления определителя матрицы, то в дальнейшем вам не потребуется проделывать это снова. Оно станет частью ва¬
шего варианта языка. Различные версии (диалекты) языка можно развивать для удовлетворения потребностей каждого про¬
граммиста. Форт не очень удобен для сложных действий с числами, если не расширить его специальными словами, то же
относится и к работе с символьными строками. Но если уж вы введете эти расширения, то они будут как раз тем, что вам
нужно, поскольку вы сделали их сами. В некоторых поставляемых версиях языка Форт заранее определено больше слов, в
других — меньше. Основная цель этой книги состоит в том, чтобы показать вам, как можно приспособить версию языка, с
которой вы работаете, к вашим задачам, например, к обработке символьных строк. Чтобы использовать всю мощь языка
Форт, вам необходимо сделать впрок некоторый программный вклад, зато потом вы получите единственный в своем роде
язык, который лучше всего подходит для ваших задач.
Верно, что читать чужую Форт-программу может быть трудно. Но в этом вина программиста, а не Форта. Если вы при¬
меняете загадочные имена для своих слов или записываете их определения очень плотно, не предусматривая пробелов для
выделения строк, подчеркивающих логический поток программы, или слишком ленивы, чтобы включать комментарии, то
можно гарантировать, что ни вы и никто другой не сможет понять то, что написано. Но если вы стараетесь дать каждому
слову общепонятное имя, записываете определения, применяя, где нужно, пробелы, отступы и разбиение по строкам для
улучшения удобочитаемости, и, кроме того, побеспокоились о включении в программу комментариев, то вашу Форт-про¬
грамму можно будет прочитать так же просто, как обычную английскую прозу.
Форт — это язык, который имеет огромную мощь и гибкость. От программиста же ожидается готовность приспособить
язык к своим индивидуальным потребностям, а также глубокое продумывание и тщательное программирование, чтобы напи¬
санное вами имело смысл.
Глава 2
СТЕК
Основная задача гл. 1 состояла в первом знакомстве с языком Форт. Но для того, чтобы пользоваться этим языком прак¬
тически, мы должны вернуться немного назад и снова, но уже более внимательно рассмотреть то, с чем мы кратко ознакоми¬
лись в предидущей главе. Мы приносим вам извинения за неизбежный повтор некоторых уже известных вопросов, но повто¬
рение способствует лучшему пониманию. Первый объект, на который мы должны посмотреть более внимательно, это стек.
Хотя мы уже говорили, что компьютеры применяются не только для арифметической и математической обработки дан¬
ных, но и во многих других областях, все равно работа производится с числами в той или иной форме. Форт выполняет ма¬
нипуляции с числами главным образом в стеке. Как мы уже видели, стек — это одно из необычных и мощных средств языка
Форт. Поначалу стек может показаться вам странным, однако пользоваться стеком очень просто, если вы поймете, что это
такое и как с ним можно работать. И нельзя овладеть Фортом, не овладев сначала стеком. Стек — это динамическая область
памяти для чисел, которая постоянно используется языком Форт. В самом использовании стека нет ничего особенно удиви¬
тельного, поскольку многие языки также им пользуются (правда, незаметно для нас). Исключительно необычным делает
Форт то, что в нем стек всегда доступен для вас как с клавиатуры, так и из программы. Все числа, вводимые вами с клавиа¬
туры или поступающие с магнитного диска, фактически попадают в стек. И большая часть чисел, которые передаются из од¬
ного слова в другое, также проходят через стек. Большая часть выгод, присущих языку Форт, также связана с постоянным
использованием стека. (Этот стек обычно называют стеком пользователя, стеком параметров или стеком данных. В дей¬
ствительности Форт использует еще один стек, который называется стеком возвратов и применяется для специальных це¬
лей, о нем вы узнаете позже в другой главе.) В этой главе мы объясним, что такое стек, как в нем реализуются арифметиче¬
ские операции, как он используется в Форте, а также рассмотрим слова для перестановки чисел в стеке с целью упрощения
решения сложных задач.
Что такое стек?
Можно представить стек попросту как стопку чисел, положенных друг на друга, либо, если хоти¬
те, как колоду карт, описанную в гл. 1. Первое число, которое было введено в стек, будет нахо¬
диться на дне, последнее введенное число — на вершине стека. Числа из стека можно брать только
с вершины. В тексте мы будем показывать стек в виде столбика чисел, положенного горизонтально,
т.е. в строчку, причем дно стека находится слева, а его вершина — справа.
Загрузите в компьютер Форт и введите
1 2 3 <BK>
(обязательно отделите числа друг от друга пробелами, как показано). Теперь в стеке находятся
числа
1(ДНО)
аналогия с картами представлена на рис. 2.1. Можно представить этот стек таким образом:
1 2 3
Теперь, если ввести
. . . <BK>
компьютер выдаст на экран
22
З(вершина)
2
Рис. 2.1
3 2 1 ok
Первым из стека было взято и напечатано число 3, затем то же самое было сделано с числом 2, а
потом с числом 1. Теперь стек пуст. Обратите внимание, что числа на экране расположены в обрат¬
ном порадке по сравнению с тем, как они представлены в стеке. Вы поняли, почему? Стек языка
Форт называется стеком с организацией "последним пришел-первым вышел” (по-английски —
LIFO). Когда стек пуст, попробуйте ввести еще раз . (точка).
Тогда вы увидите
xxxx . ? Stack empty ! (стек пуст !)
Форт отреагировал сообщением об ошибке; вы попросили его что-то напечатать, а он не знает,
что напечатать (xxxxx обычно бессмысленное число, а вид сообщения зависит от версии Форта, с
которой вы работаете, мы привели сообщение, которое выдает MMSFORTH). Обратите внимание,
что Форт не выдает сообщение ”ок”, потому что выполнение вашей последней команды не было за¬
вершено (а значит, не все в порядке).
Еще раз нажмите <BK>, чтобы получить на экране новое приглашение ”ок”, хотя в этом нет осо¬
бой необходимости.
Фактически Форт готов к вводу новой информации, даже если он не дал сообщение ”ок”.
Буфер ввода
В действительности числа, которые вы вводите, не попадают в стек сразу. Все, что вы вводите с
клавиатуры, сначала запоминается в небольшой области памяти, которая называется буфером вво¬
да, в том же виде, как вы потом видите на экране. Когда вы нажимаете клавишу <BK>, буфер вво¬
да интерпретируется, т.е. сканируется слева направо. Интерпретатор просматривает каждую
строчку символов, разделенных пробелами. Если он опознает символ(ы) как слово, он принимает
его и просматривает следующий набор символов. Если символы не опознаются как слово, интерпре¬
татор пробует, не являются ли они числом, и, если это так, помещает в стек число . Если же сим¬
волы не воспринимаются ни как слова, ни как числа, Форт выдает сообщение об ошибке.
Попробуйте ввести
1 5 9 ZZZZZ <BK> (либо другое бессмысленное слово)
вы увидите:
zzzzz ?
или какое-либо сообщение, свидетельствующее о том, что Форт вас не понял. Если теперь вы вве¬
дете . (точку), Форт напечатает сообщение ’’Stack empty !” (стек пуст).
Когда Форт не может интерпретировать слово, он производит очистку стека. Это необходи¬
мо помнить потому, что иначе вы будете обескуражены результатом, а отчасти потому, что таким
путем удобно очищать стек. Помните также, что, если Форт встречает неизвестное ему слово, он
игнорирует все, что следует за ним в строке ввода.
Буфер ввода имеет определенную длину, которая зависит от используемой версии Форта. В соот¬
ветствии с требованиями стандартов буфер должен принимать не менее 80 символов.
Стек в арифметических операциях
Как вы уже видели, помещение чисел в стек означает попросту ввод их с клавиатуры через про¬
бел или несколько пробелов. Но ради простого помещения чисел в стек и его просмотра не стоит
использовать компьютер. Основная часть работы на компьютере связана с операциями над числами,
поэтому давайте попробуем проделать некоторые арифметические действия (если вам покажется,
что мы повторяем гл. 1, пожалуйста, перетерпите это вместе с нами). Введите
3 + 5 .
и вы получите бессмысленный ответ. Форт не любит таких выражений. Для него нужно сначала по¬
местить оба числа в стек, прежде чем произвести сложение. Попробуйте ввести
3 5 + .
теперь вы получите
8 ok
т.е. правильный результат. Как вы уже знаете из гл. 1, Форт использует не такую математическую
нотацию, как другие языки. *
Существуют три наиболее распространенные нотации для представления действий с числами.
Префиксная нотация записывается так : ” + 3 5 ”. Хотя она не получила распространения в ариф¬
23
метике, фактически это самая обычная нотация, поскольку ее применяют в повседневной речи.
Коща вы говорите ’’сложите 3 и 5”, вы фактически применяете префиксную нотацию. При префик-
сной.нотации оператор стоит перед числами (операндами). (Оператор — это символ или инструк¬
ция, которая определяет операцию над одним или более числами, например, +, —, x, ’’сложить”,
sin и log — это все операторы.) Инфиксная нотация записывается так :
” 3 + 5 ”
это наиболее употребительная алгебраическая нотация, при которой оператор располагается между
числами-операндами. В языке Форт используется постфиксная нотация. Например, в выражении
” 3 5 + ” оператор стоит на последнем месте после чисел-операндов. Префиксная нотация называет¬
ся иноща польской, в честь польского математика Лукашевича, который предложил ее для фор¬
мальной логики. Постфиксная нотация обычно называется обратной польской записью (поскольку
она обратна префиксной нотации). Она используется в калькуляторах фирмы Hewlett-Packard и не¬
которых других. Инженеры и ученые обычно предпочитают такие калькуляторы, потому что, как
Мы далее увидим, не нужно беспокоиться о скобках, как при инфиксной нотации. Если вы привык¬
ли к алгебре, постфиксная нотация поначалу вам покажется странной. На самом деле в ней нет ни¬
чего странного. В школе вас, наверное, приучили к решению задач на сложение в такой форме :
з
5 +
8
Но ведь это то же самое, что
3 5 + . <BK> 8 ok
Хотя учитель, возможно, читал это выражение как ”три плюс пять равно восемь”, запись гово¬
рит вам другое : возьми 3 и 5, сложи их, получишь 8. Действительно это одна из форм стековой но¬
тации, причем числа 3 и 5 заменяются на 8. Стековая природа становится еще более очевидной для
скользящего итога, например:
5
2 +
7
3 -
4
где горизонтальная черта служит для того, чтобы показать изменяющееся состояние стека, это что-
то вроде смены кадров в кино. Совсем не случайно детей приучают к такой форме, она проще восп¬
ринимается, поэтому арифметика на языке Форт оказывается простой как для нас, так и для ком¬
пьютера.
Может быть, это и очевидно, но вот что происходит в примере со скользящим итогом на языке
Форт. Когда вы ввели
5 2 + 3 - .
интерпретатор поместил 5 и 2 в стек. Знак + снимает оба числа из стека, вычисляет их сумму и по¬
мещает в стек 7. Затем число 3 из стека помещается сверху числа 7, а оператор — вынимает из
стека числа 7 и 3, производит вычитание и помещает в стек 4. Наконец оператор . (точка) вынима¬
ет из стека число 4 и представляет его на экране. (Отметьте себе, что в дальнейшем мы не будем
говорить вам, когда нажать клавишу <BK>, означающую ввод, если это очевидно.) Другой способ
выполнить последний пример состоит в том, чтобы ввести
5 2 3 - +
Вы должны проделать все, что делает интерпретатор, и проследить, что получится в стеке, чтобы
понять, почему в обоих случаях получается одинаковый результат. Можете ли вы перестроить чис¬
ла и операторы в стеке по-другому, чтобы получился тот же самый результат?
Какой бы ни была простой и изящной постфиксная запись на языке Форт, она может использо¬
ваться в контексте с алгебраической записью. Невозможно отрицать, что алге!браическая запись по¬
лезна для выражения сложных формул, почему же тогда мы выступаем против нее в компьютерном
языке и применяем постфиксную запись? По двум причинам. Алгебраическая запись избыточна, а
для постфиксной требуется меньше места в памяти, кроме того, она также экономит и время ком¬
пьютера.
24
Экономия времени и места в памяти переходят в мощность ЭВМ. Вот пример неопределенности в
алгебраической записи. Пусть имеется такая запись :
2 x 3 + 4 x 5 = ?
Возможно несколько ответов в зависимости от порядка, в котором выполняются операции умно¬
жения и сложения. Можно избежать неопределенности двумя способами: используя скобки или при¬
писывая операторам пррядок следования. На бумаге можно использовать скобки, например:
(2 x 3) + (4 x 5) = 26
или
(2 x 3 + 4) x 5 = 50
Если вы программируете на Бейсике, то наверняка знаете, что в нем разрешается пользоваться
скобками. Если скобок нет, то и Бейсик, и большинство других языков программирования во избе¬
жание двусмысленности приписывают операторам определенный порядок следования, в данном слу¬
чае первыми производятся операции умножения, а затем операции сложения. Таким образом, если
не применять скобок, то результат приведенного примера на Бейсике будет равен 26. Необходи¬
мость в скобках или порядке следования операции приводит к тому, что алгебраическая нотация
снижает скорость операций на ЭВМ. Чтобы сам компьютер решил, как интерпретировать выраже¬
ние, ему требуется немалое дополнительное время, а для более подробного указания компьютеру
порядка его действий требуется дополнительная память для размещения кода программы. В интерп¬
ретирующих языках типа Бейсика это время затрачивается при исполнении программы. В компили¬
рующих языках типа Фортрана или Паскаля это время тратится во время компиляции. Если вы все
еще не убедились в том, что постфиксная запись не приводит к неопределенности, вам помогут не¬
сколько упражнений.
Упражнения
Прежде чем двигаться дальше, вы должны освоить применение Форта в качестве калькулятора. И не столько для того,
чтобы освоить арифметические операции, сколько для того, чтобы научиться прослеживать состояние стека в уме.
1. Переведите следующие выражения из алгебраической нотации в постфиксную и найдите ответы с помощью компьюте¬
ра. Нужно быть уверенным, что вы вводите числа в правильной последовательности, и в некоторых случаях вы не сможете
ввести все числа, не выполнив сначала некоторых операций.
Зафиксируйте ваши ответы на бумаге по мере выполнения упражнений (записи потребуются для упражнения 2).
а) 5 + 5 + 5
б) 5 4
в) (5 + 5) x 5
г) (5 + 5) / 5
д) 0.5 x (5 + 5)
е) 10 / (5 + 5) (наши числа целые .. найдите выход)
ж) (5 + 4) / (5 + 5)
з) 5 x 5 + 5 x 4 + 4 x 4
и) (5 + 4) x (5 + 4)
к) (5 + 4)2
2. Проделайте примеры из упражнения 1 по-другому, т.е. перестроив числа и операторы.
3. Раскрывая алгебраическое выражение, нужно начать с выражения в скобках, которое находится в самой глубине этого
выражения. Опишите последовательность, в которой вы будете делать вычисление следующего выражения :
16 x (2 x ((3 + 5) / 4)) = ?
Например, первой операцией должно быть сложение 3 и 5. Теперь переведите вашу последовательность действий в по¬
стфиксную форму и найдите решение на Форте. Вы убедитесь, что постфиксная форма не приводит к неопределенности.
4. Оцените значение следующих выражений, как вы это делали в упражнении 3.
а) (2 + (5 x (9 + 3) / 6)) 32 = ?
б) (32 x (2 x (3 x (5 + 6) x 3))) = ?
в) 2 x ((22 x (5 + 4)) + (2 + 5 x 10))) = ?
5. Рассмотрим пример (к) из упражнений 1 и 2. Скорее всего, вы решали его следующим образом:
5 4 + 5 4 + А.
Как вы уже знаете, имеется слово DUP, которое делает копию числа, находящегося на вершине стека, т.е.
4 3 DUP
приводиг к тому, что Б стеке останется
4 3 3
25
Можете ли вы сделать упражнение, используя DUP и вводя только по одному разу числа 5 и 4? Можете ли вы использо¬
вать слово DUP в остальных упражнениях? Слова для манипуляций в стеке очень полезны, они упрощают операции, эконо¬
мя время и память.
Манипуляции в стеке
Хотя Форт нетрудно использовать в качестве калькулятора, вы поняли из упражнений, что было
бы полезно иметь средства для перемещения чисел внутри стека. Эта потребность обеспечивается
словами для стековых манипуляций, которые приведены в табл. 2.1. Все они в совокупности позво¬
ляют вам перестраивать числа в стеке, как вы захотите.
Однако, прежде чем перейти к их изучению, полезно определить слово, которое позволит про¬
сматривать содержимое стека, не разрушая его.
Слово для просмотра содержимого стека
В некоторых версиях языка Форт имеется служебное слово для представления содержимого стека
без разрушения. Будучи полезным для црограммирования, оно может быть просто необходимым для
обучения пользования стеком. Приведенное ниже описание слова .S (печатать_стек) предназначено
для тех, у кого его нет в их версии Форт. Попробуйте ввести его (как оно работает, вы поймете к
концу этой главы). В стандарте Форт-79 слово .S определяется как
.S DEPTH ?DUP 0= IF ’’ Stack empty" (стек пуст)
ELSE 0 00 DEPTH ROLL 0UP . LOOP THEN ;
в то время как в Форт-83 :
.S DEPTH ?DUP 0= IF ." Stack empty"
ELSE 0 DO DEPTH 1- ROLL DUP . LOOP THEN ;
(Взгляните, чтобы понять различие между этими двумя определениями, на различие в определе¬
нии слова ROLL.)
Теперь, если вы введете
1 2 3 .S
то увидите на экране
1 2 3 ok
Введите теперь:
и вы увидите
3 2 1 ok
т.е. убедитесь, что слово .S действительно ничего не изменяет в стеке. Если вы введете .S, когда
стек пуст, будет напечатано сообщение об ошибке, которое является частью определения этого сло¬
ва. Буква S часто используется в словах, относящихся к стеку. Поскольку . (точка) означает ’’напе¬
чатать”, слово .S имеет вполне логичное имя.
Слова для манипуляций в стеке
Каждое из слов, приведенных в табл. 2.1, рассматривается в этом разделе самостоятельно. Вкон-
це раздела мы приводим несколько задач на употребление этих слов, которые будут полезны для
совершенствования ваших навыков в их использовании.
Прежде чем продолжить описание приведенных в таблице слов, обратим внимание на обозначе¬
ния в скобках. Как вы уже видели в гл. 1, в Форте принято применять такие обозначения (они на¬
зываются диаграммой состояния стека), чтобы показать состояние стека до и после применения
какого-либо слова. Правило хорошего тона — помещать такую диаграмму сразу после имени опре¬
деляемого слова. Поскольку все, что заключено с двух сторон в скобки (если только после открыва¬
ющей скобки стоит пробел), Форт при вводе игнорирует, скобки можно использовать для помеще¬
ния комментариев к Форт-словам. Вот, например, определение слова для сложения двух чисел и
умножения на третье число :
: +* (n1 n2 n3 — n) + * ;
26
Таблица 2.1. Перечень слов для манипуляций в стеке
Слово
Состояние стека
( до — после)
Действие
DROP
(n —)
Очищает вершину стека
DUP
( n — n n)
Делает копию числа на вершине стека
SWAP
( n1 n2 — n2 n1)
Переставляет местами два числа
OVER
( n1 n2 -- n1 n2 n1)
Копирует второе число на вершину
ROT
( n1 n2 n3 — n2 n3 n1)
Перекладывает третье число на вершину
n PICK
( n1 . . .— n1 . . . n1)
Кладет на вершину копию n-го элемента
n ROLL
( n1 . . .— . . . n1)
Перекладывает n-й элемент на вершину
7DUP
( n — n n) или ( 0 — 0)
Выполняет операцию DUP, если n ==0
DEPTH
( • • • — n)
Возвращает в стек n - число элементов
Если мы вводим 3 4 5 +*, то до того, как слово будет исполнено, nl=3, n2=4, n3=5. После опера¬
ции умножения в стеке находится п, которое имеет значение 27. Перечень принятых обозначений
для содержимого стека приведен в табл. 2.2.
Таблица 2.2. Обозначения для содержимого стека
Символ
Значение
n , n1
16-разрядное целое число одинарной длины
d,d1
32-разрядное целое число двойной длины
u
16-разрядное число без знака одинарной длины
ud
32-разрядное число без знака двойной длины
char
или с
7-разрядное представление кода символа ASCII
byte
или b
8-разрядное число, байт
флаг
или f
1 или 0 - булев флаг
адр,
адр1
Адреса
$ или $адр
Адрес, где находится строка символов
Если вы пока еще не усвоили эти обозначения, не огорчайтесь: они станут вам понятнее в после¬
дующих главах и мы привели их для использования в последующих ссылках. Обычно ход програм¬
мы становится более понятным, если вы применяете более содержательные описатели в диаграммах
состояния стека. Например, скорость можно обозначить как ”скор.”, а адрес переменной, содержа¬
щей значение скорости, — как ”адр.скор.” и т.д. Другой более полный набор символов приведен в
стандарте Форт-83, но лучше всего все-таки использовать содержательные обозначения.
А теперь вернемся к рассмотрению каждого из слов для манипуляций в стеке.
DROP — это самое простое слово; оно просто очищает вершину стека, например, после ввода
1 2 3 DROP .S
мы получим
1 2 ok
Помимо того, что слово DROP полезно для уничтожения неправильно введенных данных при вы¬
числениях (что-то вроде клавиши очистки регистра в калькуляторе), оно чаще всего используется
для того, чтобы убрать какие-либо числа из стека во время выполнения программы. Очевидно, при
пустом стеке мы получим
xxxx DROP ? Stack EMPTY I (стек пуст')
или аналогичное сообщение об ошибке. Вас может удивлять, почему Форт не знает о том, что стек
пуст, и почему он не игнорирует слово DROP в этом случае. Дело в том, что проверка на наличие
ошибок и принятие решения, игнорировать их или не игнорировать, занимает немало времени. В
отличие от большинства других языков программирования Форт состоит из полуавтономных проце-
ДУР — (слов языка Форт), каждое из которых оптимизировано с точки зрения быстродействия. Если
бы в каждое из них было включено принятие решений об ошибках, исполнение слов происходило
бы с более низкой скоростью. В Форте принято, что программист, а не язык несет ответственность
за предотвращение ошибок. Форт дает вам полный контроль над компьютером, но в то же время и
27
всю полноту ответственности. С другой стороны, разрабатывая программу, вы будете отлаживать
каждое ее слово, легко обнаруживая ошибки. И поэтому Форт не нуждается в сложных и требую¬
щих больших затрат времени методах контроля ошибок, необходимых другим языкам.
Нетрудно догадаться по названию, что слово DUP (по-англ. ’’DUPlicate ” — копировать) делает
копию верхнего элемента стека.
9 DUP .S
приводит к тому, что в стеке окажется
9 9 ok
Слово DUP, пожалуй, одно из наиболее часто употребляемых слов для манипуляций в стеке. Вы
встречались с ним в одном из упражнений гл. 1. Часто программе требуется одно и то же значение
несколько раз. Слово SWAP также, судя по названию (от swap — обменивать), переставляет места¬
ми два верхних элемента стека. Если ввести
3 18 .S
вы получите на экране
18 3 ok
что особенно полезно, если нужно сделать операцию вычитания или деления, но операнды стоят в
неправильном порядке. После слова DUP оно применяется наиболее часто. Однако не допускайте,
чтобы это сделало вас ленивым. Иногда проще применить слово SWAP перед операцией деления
или вычитания вместо того, чтобы заранее предусмотреть в программе требуемый порядок следова¬
ния чисел в стеке. Использование SWAP может ускорить составление программы, но на этапе ис¬
полнения увеличит время работы программы.
Действие слова OVER не очевидно из его названия (через). Попробуйте ввести
1 2 OVER S
и вы увидите
1 2 1 ok
Можно понять, что второе число в стеке было скопировано на вершину стека. Другими словами,
OVER как бы заставляет второй элемент стека ’’перепрыгнуть” через первый элемент на вершину.
Оно очень похоже на слово SWAP, но в отличие от него не удаляет второй элемент стека. Слово
может быть полезно во многих случаях, когда какое-либо число должно быть использовано несколь¬
ко раз. В следующем примере дублируются два верхних элемента стека :
1 2 OVER OVER S
при этом получается
1 2 1 2 ok
Дублирование двух верхних элементов стека очень полезно, когда в стеке нужно оставить для
последующего использования два числа. Предположим, что вам нужно вывести на экран одновре¬
менно и сумму, и произведение чисел 5 и 7.
5 7 OVER OVER + . *
выдает в результате
12 35 ok
Конструкция OVER OVER полезна также для сравнения двух чисел. Дальше мы узнаем, что су¬
ществует слово 2DUP, которое выполняет ту же функцию, что и OVER OVER. Имеется целое се¬
мейство слов, которые оперируют в стеке парами чисел, но, так как они используются главным об¬
разом для операций с так называемыми числами двойной длины, мы их рассмотрим в гл.4.
Слово ROT (по-англ. ”rotate” — поворачивать) производит ротацию трех верхних чисел стека,
т.е. перекладывает третье сверху число в стеке на его вершину, так что
1 2 3 ROT .S
приводит к
2 3 1 ok
Третье сверху число в стеке перемещается на вершину стека, а следующие два числа продвига¬
ются на одну позицию в глубь стека. Применения слова ROT поначалу не очевидны, но они также
важны. Предположим, что вы хотите определить значение выражения
(5 + 2) x (7 + 3), если в стеке находится
5 2 7 3
Для решения введите
+ ROT ROT + x .
и вы получите
70 ok
28
Действие некоторых комбинаций слов, вроде приведенных в этом примере, достаточно трудно по¬
нять даже опытному программисту. Поэтому проследить, что происходит в стеке в последнем при¬
мере, вам поможет следующая таблица :
Операция
Содержимое стека
(до — после)
Операция
Содержимое стека
(до — после)
(стек в начале)
(5 2 7 3 —)
+
(— 10 7)
+
(— 5 2 10)
x
(-- 70)
ROT
(-- 2 10 5)
(— стек пуст)
ROT
(— 10 5 2)
Если требуется произвести сложные манипуляции в стеке вроде приведенного, составление такой
таблицы может оказаться полезным.р Наиболее часто возникновение ошибок в определении слова
(программы) происходит при манипуляциях в стеке.
В отличие от предыдущих слов PICK требует аргумента — числа, которое указывает, какое по
счету число сверху вы хотите скопировать на вершину стека. Аргументом является номер числа, за
начало принимается вершина стека, а счет ведется по направлению в глубь стека. Так, например, в
стандарте Форт-79 операции
1 2 3 4 5 6 4 PICK .S
приводят к результату
1 2 3 4 5 6 3 ok
Но в стандарте Форт-83 вы увидели бы
1 2 3 4 5 6 2 ok
Это принципиальное и зачастую очень неудобное различие стандартов Форт-79 и Форт-83. В
форт-79 принято, что верхний элемент стека имеет номер 1, а в Форт-83 — номер 0. Таким обра¬
зом, в стандарте Форт-83 выражение
0 PICK
приводит к такому же результату, что и DUP, в то же время для Форт-79 стандартное слово DUP
эквивалентно
1 PICK
Если ввести 0 PICK, в стеке окажется какая-нибудь чушь. Таким образом, PICK, доставая число
из глубины стека, не производит в нем других изменений.
Слово ROLL похоже на PICK, но в отличие от него вынимаемое число при перемещении его на
вершину на старом месте удаляется. Например, в стандарте Форт-79
1 2 3 4 5 6 5 ROLL .S'
приведет к
1 3 4 5 6 2 ok
в то же время та же операция в стандарте Форт-83 приведет к другому результату:
2 3 4 5 6 1 Ok
Нумерация элементов в стеке в стандартах Форт-79 и Форт-83 отличается для оператора ROLL
так же, как и для PICK. Эти различия в стандартах важно учитывать при переносе программ из од¬
ной версии в другую. Слова ROLL и PICK следует применять, если невозможно использовать ниче¬
го другого, так как они работают значительно медленнее, чем DUP, OVER и ROT (эти три слова
определены не с помощью слов PICK и ROLL, а непосредственно в машинных кодах). К тому же
слово ROLL работает значительно медленнее, чем PICK. Чтобы избежать путаницы, нужно де¬
ржать в стеке не более четырех чисел, которые используются в данном слове, и, если вы будете
придерживаться этого правила, вам редко потребуются слова PICK и ROLL.
А сейчас будет уместно сделать небольшой экскурс в стандарты языка Форт. Как уже указыва¬
лось во введении, существуют два известных стандарта — Форт-79 и Форт-83. Ссылки на основные
документы, описывающие стандарты, приведены в приложении В. Различия между стандартами за¬
частую невелики, однако имеют значение, как, например, в случае ROLL и PICK, и мы обратим на
это ваше внимание, когда будем встречаться с этими различиями. Версия, с которой вы работаете,
должна соответствовать одному из стандартов, и вы можете определить, какому именно, если вве¬
дете 79-STANDARD или FORTH-83. Если без ошибки будет принято первое слово, то ваша версия
совместима с Форт-79, если же без ошибки будет принято последнее слово, ваша версия соответст¬
вует стандарту Форт-83.
29
Слово ?DUP представляет собой специальный вариант слова DUP. Оно делает копию числа, на¬
ходящегося на вершине стека, если оно не равно нулю, и не копирует число, если оно равно нулю.
Например,
1 4 5 ?DUP S
дает в стеке
1 4 5 5 ok,
в то время как
1 4 0 ?DUP .S
приводит в результате к
1 4 0 ok
Таким образом, в посЛеднем случае слово 7DUP не производит никаких действий. Оно особенно
полезно вместе с конструкцией IF...THEN, примеры этого будут приведены в последующих главах.
Последнее слово в табл. 2.1, DEPTH, не производит никаких перестановок в стеке. Вы уже
встречали его в гл. 1. Оно подсчитывает количество чисел в стеке и выдает его на вершину стека.
Если ввести
• 21 131 56 7 89 DEPTH
мы получим
5 Ok
где 5 — это число элементов, находившихся в стеке перед ис-полнением слова DEPTH. Мы увидим
еще применение слова DEPTH в последующих упражнениях.
Упражнения
Они могут показаться вам вначале очень простыми, но потом будут усложняться. Если даже они покажутся вам скучны¬
ми, пожалуйста, проделайте их. Одной из самых важных компонент изучения Форта является освоение работы со стеком
настолько, чтобы вы чувствовали себя с ним комфортно, а слова, связанные с манипуляциями в стеке, стали бы вашей вто¬
рой натурой. Затраченное вами время сторицей окупится впоследствии.
1. Напишите слово или слова, с помощью которых содержимое стека изменялось бы в соответствии с заданием. Старай¬
тесь обойтись минимумом слов.(Мы пользуемся здесь буквами вместо чисел, чтобы у вас возникла уверенность, что эти при¬
меры пригодятся и в более общих ситуациях.)
До После
До
После
До
После
(а)
а b b а
(6)
а b
а b b
(в)
а b
а b а
(г)
а b а а b
(д)
а b
b b а
(e)
а b с
b C а
(ж)
а b с а с b
(3)
а b
с
с b а
(и)
а b с
b а с
(к)
а b с с а b
(л)
а b
с
а b b с
(м)
а b с
а а b с
(н)
а b а а b b
(о)
а b
с
а а b b
с с
2. Принимая содержимое стека таким, как показано, напишите слово или слова для вычисления выражений. Снова ста¬
райтесь обойтись минимальным количеством слов.
Стек
Выражение
Стек
Выражение
(а)
а b
ab (6)
а b
ab^
(в)
а b2
a+b (г)
а b
а b
(Д)
а b
apbJ р (e)
а b
a(a + b)
(ж)
а b
a+2ab+b (указание
разложите на
множители)
(з)
а b с
(а + b)/c (и)
а b С
(a+b)/(b+c)
(к)
а b с
(a+c)/(b+c) (л)
а b с
(a+b)/(2c)
(м)
а b с
a/(2c)+b/(2c) (н)
а b с
(b-a)(b-c)
(о)
а b с d
ab+ac+ad (п)
а b с d
ab+abcd
Не применяйте ROLL и PICK !
3. Напишите новое определение слова OVER (NEWOVER), ис-пользуя другие слова.
4. Слово 2DUP выполняет следующие операции в стеке :
( n1 n2 — n1 n2 n1 n2)
30
Оно применяется для чисел двойной длины. Напишите определение этого слова под именем NEW2DUP, используя для
этого только два слова.
5. Определите слова DUP и OVER, используя PICK. Проделайте это для обоих стандартов. Дайте этим словам новые име¬
на во избежание путаницы.
6. Определите ROT, используя слово ROLL. Проделайте это для стандартов Форт-79 и Форт-83. Дайте этому слову новое
имя.
7. Слово 2SWAP выполняет в стеке следующие операции :
( n1 n2 n3 n4 — n3 n4 n1 n2).
Определите это слово на Форт-79 и Форт-83.
8. Определите слово ROTSTACK, которое производило бы ротацию элементов стека независимо от его длины. Если в сте¬
ке находится а b с d e f, то после исполнения ROTSTACK в нем должно быть b с d e f а. Используйте для этого только два
слова. Проделайте это на Форт-79 и Форт-83.
9. Объем сферы выражается формулой 4/ЗПи x r^. Напишите слово для определения объема, умноженного на 100, если в
стеке задано значение r. (Указание : используйте число Пи-3.14, умножив его на 100.)
10. Определите слово, помещающее в стек произведение двух самых нижних чисел в стеке, не разрушая содержимого
стека. Все, что вам известно : в стеке находятся два или более числа. (На практике вы не должны допускать разрастание
стека, чтобы не приходилось делать что-либо подобное.)
11. Слово MOD дает остаток от деления нацело, т.е. 11 3 MOD выдает в результате 2. Дайте определение слова MOD.
Выводы
При изучении языка Форт, возможно, наиболее важным является умение легко и быстро рабо¬
тать со стеком. К данному моменту вы должны хорошо понимать, как работает стек и как его мож¬
но перестроить, если это необходимо. После овладения операциями в стеке вам легче будет освоить
материал последующих глав. Если вы чувствуете, что вам нужно попрактиковаться еще, вернитесь
к разбору предыдущих упражнений или придумайте свои собственные задачи и решите их.
Конечно, у вас еще будет много практической работы со стеком в нескольких следующих главах.
Вскоре вы обнаружите, что сможете инстинктивно писать разумные программы, едва ли задумыва¬
ясь о состоянии стека.
Глава 3
ПАМЯТЬ, ЧИСЛА, СИМВОЛЬНАЯ ИНФОРМАЦИЯ
Вы можете работагь с языком Форт, даже очень мало понимая, как работает сам компьютер, но это будет вам постоянйо
мешать. Чтобы понимать, что в нем происходит, вы должны по крайней мере понимать, как работает память ЭВМ и как в
ней хранятся числа. Это не так уж сложно, как многие думают, а с помощью языка Форт еще проще. Не пропускайте эту
главу, даже если у вас уже есть опыт работы с ЭВМ. Можно, если хотите, бегло просмотреть более простые вопросы. Обра¬
тите, однако, внимание на возможности Форта, касающиеся его обращения с системами счисления и операциями в памяти,
которые являются его привлекательными сторонами. Возможно, что у вас возникнет также желание узнать функции слов
BASE и DECIMAL.
Что такое память?
Представьте себе, что имеется 8 переключателей, каждый из которых может быть включен или
выключен. Можно их закодировать так, чтобы состояние переключателей представлялось числами.
Будем считать, что единице соответствует включенный переключатель, нулю — выключенный. В
табл. 3.1 показаны числа, построенные таким образом. Восемь переключателей могут представлять
до 256 чисел (от 0 до 255). Если немного подумать, вы сможете доказать, что с помощью n пере¬
ключателей можно запомнить 2П чисел (28=256).
С помощью 16 переключателей или двух банков по 8 переключателей могут быть представлены
65536 чисел (от 0 до 65535).
Предполагается, что для чисел в стеке имеется два банка по 8 переключателей. Вследствие этого
наибольшее число, которое можно записать в стек и которое мы называем числом одинарной дли¬
ны, равно 65535. Для представления чисел двойной длины нужно уже 32 переключателя, и диапа¬
зон представляемых чисел увеличивается до 65535 x 65535 = 4271406736.
Итак, мы видим, что память микрокомпьютера — это не что иное, как большое количество пере¬
ключателей, которые могут включаться и выключаться с огромной скоростью. Вот так. И все, и ни¬
чего больше. Сложность здесь состоит в том, чтобы организовать переключатели для хранения чи¬
сел и, следовательно, данных. (Алфавитно-цифровой текст также можно представить в виде закоди¬
рованных чисел. Например, буква А представляется числом 65.) Каждый переключатель в памяти
называется битом, от английского Binary digiT (двоичный разряд, а почему — мы вскоре узнаем).
А теперь можете забыть о всяких переключателях, будем говорить'только о битах. Если значение
содержимого бита равно 1, то говорят, что он взведен или установлен; если значение равно 0, то
говорят, что он сброшен или выключен. Многие недорогие ЭВМ, например Commodore 84, TRS-80
модель 4, Apple II, используют ЦПУ, которое работает с 8-разрядными числами; их называют 8-
разрядными машинами, они обеспечивают доступ к 65536 8-разрядным ячейкам памяти, т.е. к
524288 битам. Более дорогие 16-разрядные машины, например IBM PC и Tandy 2000, работают с 16
разрядами одновременно, обеспечивают доступ к большему числу ячеек памяти, объем памяти
здесь, скорее, ограничивается потребностями пользователя и его финансовыми возможностями. 8-
разрядный элемент памяти называют байтом, в байтах принято оценивать емкость памяти. В ЭВМ
ранних моделей за единицу емкости принималось четыре разряда.
16 разрядов иногда называют словом, однако, так как слово на больших ЭВМ может быть 32-раз-
рядным или даже 64-разрядным и, кроме того, понятие ’’слово” в языке Форт имеет совсем другой
смысл, будем называть 16-разрядное число ячейкой либо просто числом. Это число — целое, ибо
оно используется для хранения в стеке чисел одинарной длины. Для обозначения больших объемов
памяти применяются слова с префиксами кило и Мега-, которые в метрической системе означают
соответственно 1000 и 1 миллион.
Например, километр — это 1000 метров, 1 МегаГерц — это 1000000 Гц, или 1 млн. колебаний/с.
В вычислительной технике применяются другие единицы (не столь строгие). Килобайт (сокращает¬
ся как Кбайт) — это приблизительно 1000 байтов, на самом деле он равен 210 = 1024 байта. Мега¬
байт (сокращенно Мбайт) используется еще более нечетко. Иногда считают его равным 1000 Кбай¬
там, т.е. 1024000 байтам, а иногда 1024 Кбайтам, т.е. 1048576 байтам. Последнее число представля¬
ется как 220 байтов. Это полезная единица для обозначения объема памяти, и мы будем использо¬
вать именно это определение. Так, например, модели ЭВМ TRS-80 Modell III и Apple II обычно на-
32
зывают машинами с объемом памяти 64 Кбайта или 64K- машинами; они могут работать с памятью
объемом 64 x 1034 = 65536 байтов; IBM PC может работать с памятью объемом до 1 Мбайта, или
1048576 байтов.
Таблица 3.1. Двоичные числа
Состояния переключателей
Число
Состояния переключателей
Число
00000000
0
00000110
6
00000001
1
00000111
7
00000010
2
00001000
8
00000011
3
00001001
9
00000100
4
00000101
5
11111111
255
Двоичная запись и основание системы счисления
Кое-что об этом вы уже знаете. Двоичная запись — это попросту соглашение о записи чисел с
помощью только двух цифр 0 и 1 (отсюда двоичный разряд, или бит). Люди привыкли к десяти¬
чной арифметике, которая использует 10 цифр (or 0 до 9) или так называемой десятичной форме
записи, но компьютеры имеют дело с единицами и нулями, т.е. с двоичной системой. Как мы вско¬
ре увидим, иногда (но не очень часто) удобно работать с числами, использующими для записи 8
цифр (от 0 до 7), т.е. восьмеричную форму, а очень часто удобнее использовать 16 чисел (от 0 до 9
и от А до F), или так называемую шестнадцатеричную форму представления. В шестнадцатеричной
форме число 10 обозначается буквой А, 11 — буквой В и т.д., число 15 — буквой F, число 16 обоз¬
начается как 10. Давайте снова рассмотрим таблицу двоичных чисел. Посмотрите, сможете ли вы
объяснить, почему для обращения с байтом оказывается удобной шестнадцатеричная система (для
этого продолжите таблиЦу до 16). Если вы не догадались, то через некоторое время это станет по¬
нятнее. Хорошее практическое обсуждение оснований систем счисления и других аспектов пред¬
ставления чисел вы можете найти в книге Липшуца ’’Арифметические основы компьютеров”
(1982).
Количество различных цифр, используемых для представления чисел, называется основанием си¬
стемы счисления или просто основанием. При двоичной записи основание равно 2, при восьмерич¬
ной — 8, при десятичной — 10, при шестнадцатеричной — 16 (обозначение ее в Форте HEX проис¬
ходит от английского названия hexadecimal). Прелесть Форта состоит в том, что, хотя все числа он
хранит в двоичной форме (впрочем, как и многие другие языки, что определяется требованиями
компьютера), он может принимать их и отображать с любым основанием вплоть до 72. Нам потре¬
буются некоторые слова Форта, которые позволяли бы отображать числа из стека в различных сис¬
темах счисления, но прежде чем мы с ними познакомимся, необходимо немного отвлечься, чтобы
рассмотреть, как мы можем извлекать байты и числа, которые хранятся в памяти. С каждым бай¬
том сопоставляется число, начиная с нуля и больше, которое соответствует его положению в памя¬
ти и называется адресом. Таким образом, первый байт имеет 0-й адрес в памяти, в то время как
для 8-разрядной ЭВМ адрес самого верхнего байта равен 65535. Для осуществления доступа к со¬
держимому указанного адреса в Форте предусмотрено несколько слов. Наиболее важными из них
являются @ (извлечь) и ! (занести, запомнить). Если в стеке на вершине находится адрес, то слово
@ замещает адрес 16-разрядным (двухбайтовым) числом, которое хранится по этому адресу. Если
так же на вершине находится адрес, а вторым элементом стека является число, то оператор ! про¬
изводит запоминание этого числа по указанному адресу. Слово ! следует употреблять осмотритель¬
но, так как можно изменить содержимое важной части памяти, если вы запишете не в то место па¬
мяти. Можно использовать слова @ и ! для того, чтобы узнать систему счисления или изменить ее
при вводе и выводе чисел. В памяти имеется ячейка (адрес), в которой хранится основание системы
счислений, которое действует в настоящий момент при вводе-выводе чисел. Этот адрес выдается в
стек, когда’ вы вводите слово BASE. Давайте его испытаем. Вначале дадим компьютеру задание
принимать и выводить числа в десятичной системе счисления с помощью слова DECIMAL (десяти¬
чный). Слово DECIMAL изменяет число, которое хранится по адресу, возвращаемому в стек словом
BASE, на 10 (десятичная система). Теперь основание может быть сделано шестнадцатеричным (ос¬
нование 16), если ввести
2 М. Келл:
33
16 BASE !
Слово BASE помещает на вершину стека соответствующий адрес, затем в этот адрес записывает¬
ся число 16. Заметим, кстати, что слово BASE — это особая переменная, так называемая перемен¬
ная пользователя. Переменная — это слово, которое выдает адрес, где может храниться число; мы
обсудим ее более детально в гл. 6. Форт не нуждается в большом количестве переменных, как дру¬
гие языки программирования, так как он может хранить числа в стеке. Теперь, когда компьютер
использует шестнадцатеричную систему счисления, вы можете ввести
1B DECIMAL .
и получите
12 ok
Число 12 в десятичной записи — это то же самое, что lB в шестнадцатеричной. Следующий при¬
мер произведет обратное действие:
13 16 BASE ! .
выдаст на экране lC, т.е. шестнадцатеричный эквивалент числа 13. Слово HEX, которое не являет¬
ся стандартным, включено в большинство версий Форта. Оно устанавливает шестнадцатеричную
систему счисления так же, как DECIMAL устанавливает десятичную. Во многих версиях есть слова
OCTAL и BINARY, которые устанавливают систему счисления 8 и 2 соответственно. Можно проил¬
люстрировать один важный момент с помощью следующих экспериментов. Попробуем ввести
DECIMAL 2 BASE ' BASE @
потом
DECIMAL 8 BASE 1 BASE @
И
DECIMAL 16 BASE '.BASE@
Во всех случаях вы увидите 10. Почему? Основание числа во всех случаях представляется как
10, так как это — два (в двоичной системе), 8 (десятичное) — это 10 в восбмеричной системе и 10
(десятичное) представляется так же, как 10 в шестнадцатеричной системе. Как же тогда узнать, в
какой системе мы находимся? Вот слово, которое поможет это сделать:
BASE? BASE @ DUP DECIMAL . BASE ! ,
Вы должны понимать, как оно работает. Если вы введете
16 BASE ! BASE9
то увидите, что на экране будет число 16, и после этого мы по-прежнему останемся в шестнадцате¬
ричной системе счисления. Вместе с тем, когда вы вводите
‘ n BASE '
нужно быть внимательным и твердо знать, от какого основания мы переходим. Например, если мы
находимся в двоичной системе, то при вводе
10 BASE '
ничего не произойдет. 10 в двоичной системе — это десятичное число 2, но мы уже находимся в
двоичной системе. А что произойдет, если ввести
10 BASE !
в шестнадцатеричной системе? Как снова вернуться к основанию 10? Если вы были в шестнадцате¬
ричной системе, можете ввести
А BASE !
А — это шестнадцатеричное число, которое равно 10 (десятичное). Если вы забыли текущее ос¬
нование, то слово DECIMAL всегда возвратит вас к основанию 10, независимо от того, в какой сис¬
теме вы были до этого.
Приведем слово, которое показывает в двоичной системе счисления число, находящееся на вер¬
шине стека. Обратите внимание, что при этом оно не изменяет содержимое стека:
.BIN (n — n) DUP BASE @ 2 BASE ! SWAP . BASE ! ;
Вы должны догадаться, как оно работает.
В качестве упражнения (и для использования впоследствии) опишите три слова : .OCT, .DEC и
.HEX, которые будут печатать число из стека в восьмеричной, десятичной и шестнадцатеричной си¬
стеме соответственно. Для этого вам надо изменить в слове .BIN всего один символ. Теперь мы смо¬
жем написать еще одно слово, которое даст возможность представить число в стеке одновременно в
двоичной, восьмеричной, десятичной и шестнадцатеричной системах :
.NUMS (n --) .BIN .OCT .DEC .HEX DROP ;
34
Слово .NUMS можно использовать для того, чтобы посмотреть, как различные числа представляют¬
ся в различных системах счисления. Но давайте проделаем это в следующих упражнениях. Если у
вас нет компьютера, проверьте ваши ответы по приложению Д.
Упражнения
1.Используйте .NUMS со следующими числами: 1,3,7.15,31.63,127 и 255. Подметили ли вы общий рисунок? Какая зави¬
симость существует между числом 2, возведенным в целую степень, и двоичным представлением?
2. Теперь проделайте то же самое с числами 2,4,8,16,31,64,128 и 256. Видите ли вы, почему шестнадцатеричное пред¬
ставление удобно для работы с байтами?
3. Перейдите в двоичную систему и введите восьмиразрядное число с единицами во всех разрядах. Используйте .NUMS.
Вы снова сможете убедиться в удобстве шестнадцатеричной системы. Проделайте это упражнение для 16-разрядного числа.
Вернитесь к десятичному основанию.
4. Перейдите к основанию 16 и введите 1 плюс шестнадцатеричное число, которое эквивалентно байту со всеми единица¬
ми в разрядах. Возведите это число в квадрат и выведите его словом .NUMS. Сколько используется разрядов? Почему? По¬
думайте еще раз, почему удобна шестнадцатеричная система?
5. Определите слова HEX, OCTAL и BINARY.
6. Понятно ли вам, почему, если вы хотите использовать шестнадцатеричные числа, недопустимо давать имена такого ти¬
па: AA, A4, FF и т.д. и в то же время имя GG будет приемлемым?
Некоторые программисты, которые много работают с числами, представленными в двоичной, шестнадцатеричной или
восьмеричной системах, действительно могут думать и выполнять арифметические действия в этих системах, однако боль¬
шинство из нас на это неспособны. Одной из приятных особенностей, которую мы не найдем в других языках, является воз¬
можность производить преобразование систем счисления. Программист может думать только в одной наиболее удобной сис¬
теме счисления, обычно десятичной. (По мере приобретения опыта вы убедитесь, что наиболее удобным основанием для ра¬
боты с адресами памяти является шестнадцатеричная система.)
Операции с битами
Иногда бывает необходимо изменить отдельные разряды в числе. Например, компьютер TRS-80
определяет, занят ли принтер, по состоянию одного разряда (включен-выключен), точно так же
ЭВМ TRS-80 и IBM PC определяют состояние телекоммуникационной связи через модем по состоя¬
нию (статусу) нескольких битов в трех байтах. Так, если один опрбделенный бит установлен в ”1”,
то данные принимаются по телефонной линии, но если этот бит сброшен в ”0”, то линия свободна
и можно передавать данные. Поэтому зачастую полезно иметь возможность просматривать состоя¬
ние, а также изменять значения отдельных битов. В оставшейся части этого раздела мы будем ис¬
пользовать компьютер для ввода и вывода двоичных чисел, поэтому вам нужно установить двоич¬
ное основание, т.е. записать в переменную BASE число 2. Если вашему компьютеру известно слово
BINARY, то достаточно ввести это слово. (Если вы не уверены, попробуйте его, худшее, что может
произойти, — это получение сообщения об ошибке.) Если слово BINARY у вас не работает, введите
2 BASE !
Теперь, если вы попытаетесь ввести цифру больше 1, Форт будет отвергать ее как не имеющую
смысла в двоичной системе счисления. Представим себе, что в стеке находится двоичное число
10011111 и вы хотите изменить третий справа разряд в ”0”, т.е. вы хотите превратить это число в
10011011. Слово AND (И) позволит вам сделать это.Слово AND сравнивает поразрядно два числа,
и если в* обоих (одном и другом) эти разряды установлены в ” 1 ”, то результат будет 1, иначе ре¬
зультатом будет 0. Попробуйте ввести следующее предложение:
10011111 11111011 AND U.
тогда вы увидите следующий результат :
10011011 ok
Нагляднее всего проследить, что произойдет, если поместить числа одно под другим:
10011111
11111011 AND U.
это приведет к такому результату :
10011011 ok
Второе число, 11111011, называется разрядной (битовой) маской. Разрядная маска — это всего-
навсего число, которое сравнивается с другим числом с целью изменить значение отдельных битов.
2’
35
Теперь представим себе, что вы хотите снова изменить значение разряда 0 в 1. Слово AND не
сможет этого сделать. Вместо него мы используем слово OR (ИЛИ). Оно также производит пораз¬
рядное сравнение двух чисел, но, если хотя бы один из разрядов установлен в ”1”, то в результате
тоже будет ”1”. Попробуем установить в ”1” третий разряд в последнем примере. Для этого случая
мы используем маску 00000100 :
10011011
00000100 0R и.
что приводит к
10011111 ok
т.е. к исходному числу.
Третье полезное слово XOR (исключающее ИЛИ) также производит поразрядное сравнение. Если
два разряда различны, то в бите результата устанавливается ”1”, если одинаковы, — то ”0”. Пусть
вы хотите изменить значения всех разрядов числа на противоположные, т.е., например, преобразо¬
вать число 10011011 в 01100100. Попробуйте сделать так :
10011011
11111111 X0R U.
и получите
01100100 ok
Так как в маске все разряды были установлены в ”1”, то там, где в исходном числе была 1, в ре¬
зультате стал 0, а если был 0 — стала 1.
Есть еще одно слово, NOT (НЕ), которое в различных версиях языка определено не очень четко.
В большинстве версий, в том числе в Форт-79, слово NOT не изменяет значение битов, оно делает
нечто иное, о чем мы расскажем в одной из следующих глав. Однако в Форт-83 слово NOT изменя¬
ет значение разрядов на противоположное. Таким образом,
00000000101010 N0T и
приведет к результату
11111111010101 ok
Операция NOT не эквивалентна вычитанию каждого разряда из двоичной единицы. Сможете ли “
вы определить на Форт-83 слово NOT, используя XOR? Слова AND, OR, XOR и NOT имеют и дру¬
гие применения, с которыми мы познакомимся в упражнениях.
Упражнения
1. Подберите маску и подходящее слово для того, чтобы изменить приведенные числа, как показано в таблице. Не ис
пользуйте XOR, если есть более простое решение.
Исходное число
Преобразованное число
Исходное
число
Преобразованное число
(а)
10101011
10100011
(e)
10101111
00001111
(6)
10100011
10100111
(ж)
10100000
00001111
. (в)
10100011
00000000
(з)
10101010
01010101
(г)
10100011
11111111
(и)
11110000
00001111
(д)
10101111
10100000
2. Опишите слово для преобразования любого слова, находящегося на вершине стека, в ”0”.
3. Слово - (во всех версиях, кроме Форт-83) возвращает единицу, если два верхних числа в стеке равны. Иначе оно воз
вращает ”0”. Определите слово -. (Совет: нужно дважды использовать XOR, один раз с маской и также /.)
4. B некоторых версиях Форта слово <> возвращает 1, если два верхних числа не равны, и 0 в противном случае. Опред*
лите слово <> двумя способами : с использованием
слова - и без него.
5. Опишите в Форт-83 слово NOT под именем NOTl, используя XOR. Опишите его, используя — .
6. Опишите слово, которое, если дана маска с определенными установленными в ”1” разрядами, возвращает 1 в соотве'
ствующих разрядах второго сверху числа в стеке, если их значение равно 1 и 0 в противном случае. (Совет: используйт
слово - из упражнения 3).
7. Опишите слово, которое, если задана маска с определенными разрядами, установленными в ”1”, будет возвращать 1
соответствующих разрядах второго сверху числа, если они равны 0, и 0 в противном случае.
36
Приемы, использованные в упражнениях 6 и 7, могут быть полезны для проверки состояния разрядов памяти, например
для проверки того, включен ли принтер.
Положительные, отрицательные числа и числа без знака
Для представления чисел одинарной длины в Форте используются два байта, т.е. 16 битов памя¬
ти. Поэтому возможно представить числа от 0 до 65535 (216=65536), не так ли? Но, разумеется, ес¬
ли нам не нужны отрицательные числа.
Попробуйте ввеста с клавиатуры
65535 .
Результат получится такой :
-1 ok
Что произошло? Очевидно, то, что Форт воспринимает число 65535 как отрицательное число. Те¬
перь попробуйте
65534 .
и получите
-2 ok
Это кажется еще более странным. А теперь введите
32768 .
Форт выдаст
-32768 ok
В то же время, если ввести
32767 .
мы получим
32767 ok
Ужасная путаница, неправда ли? В действительности же Форт работает с так называемыми чис¬
лами со знаком и числами без знака. Все, что находится в диапазоне 32768 — 65535, Форт рас¬
сматривает как отрицательные числа, если вы не указываете, что они положительные. Попробуйте
ввести
65535 U.
Результат
65535 ok
получится таким, каким он и должен быть. Если вы испытаете слово U. с другими числами, вклю¬
чая те, которые больше 32767, вы будете получать те же числа, что и ввели. Слово U. предназначе¬
но для печати чисел, не принимая во внимание знак числа, т.е. ’’чисел без знака”. Имеются также
другие операторы для выполнения арифметических действий с числами без знака, и мы будем рабо¬
тать с ними в гл. 4.
Ну а теперь попробуйте ввести
-1 и.
в результате получится число 65535. Становится все хуже и хуже. А вот что происходит на самом
деле. Если вы вводите 65535 или -1, Форт кладет в стек одно и то же число:
1111111111111111
В нем все 16 разрядов установлены в ”1”. Числа 65534 и -2 вводятся в стек в виде
1111111111111110
и 65533 и -3 — как
1111111111111101
Поэтому, если числа больше 32767, Форт может рассматривать их и как отрицательные и как
числа без знака, в зависимости от ваших указаний. Числа 0 — 32767 (от 0 до 11111111111111, с 15
единицами) воспринимаются как положительные. Если число со знаком, т.е. если его 16-й разряд
равен 1, тогда число считается отрицательным, но это не совсем так, потому что оказывается, что
числа ’’больше” 32767 (как числа без знака) менее ’’отрицательные”. Более понятно это станет из
упражнений.
Принятое в Форте соглашение об использовании чисел со знаком называется арифметикой с до¬
полнением по модулю два. Не вдаваясь в причины, укажем, что число в арифметике с дополнением
по модулю два получается путем вычитания каждого разряда из 1, а затем добавлением ко всему
числу 1. Образование числа с дополнением по модулю два попросту меняет его знак. Так, -10 явля¬
ется дополнением по модулю два от 10, а 10 — дополнением по модулю два от -10. Оказывается,
37
что принятое соглашение упрощает работу ЦПУ при арифметических операциях. (В большинстве
других языков также применяется целочисленная арифметика с дополнением до 2.) Вспомните, что
слово NOT в Форт-83 эквивалентно вычитанию каждого разряда числа из 1, поэтому
10 N0T 1 + .
даст в результате -10. Стандартное слово NEGATE делает следующее : оно йзменяст знак числа,
вычисляя его дополнение по модулю два. Дополнение чисел и дополнительная арифметика подроб¬
но описаны в книге Липшуца (1982).
Упражнения
Убедитесь в том, что слово .BIN находится в вашей системе. Если это не так, снова введите его (см. выше). Определите
новое слово, которое печатает двоичные числа без знака следующим образом :
. U BIN DUP BASE @ 2 BASE ' SWAP U. BASE ! ;
(В чем различие между .BIN и U.BIN?)
1. Напишите одно слово, которое показывает число, находящееся на вершине стека в виде десятичнот со знаком, десяти¬
чного без знака, двоичного со знаком и двоичного без знака.
2. Используйте слово U.BIN для того, чтобы подготовить на экране таблицу приведенных ииже чисел, показывая их u ии-
де чисел со знаком и без знака в двоичной и десятичной системе 1, 2, 3, 32766, 32767, 32768, 32769, 65533, 65534, 65535.
3. Глядя на таблицу из упражнения 2 (и если нужно, проведя дополнительные эксперименты), напишите алгебраическое
выражение для преобразования отрицательных чисел в диапазоне -1 — 32768 в положительные числа, которые выводятся с
помощью слова U.
4. Еще раз, просматривая таблицу из упражнения 2, напишите слово, которое будет менять знак числа, находящегося u
стеке, не используя -, а с помощью операций над битами (вы уже описали слово NEGATE). Как это связаио с дополнением
исходного числа по модулю два?
5. Используя результаты упражнения 4 и операцию +, напишите определение слова — для выполнения вычигапия.
Операции с байтами
Иногда говорят о наиболее и наименее значащих разрядах десятичного числа. Например, в числс
456 наиболее значащий разряд — 4, наименее значащий 6. Таким образом, 456 — это 400 + 50 + 6,
где 4 указывает, сколько в числе содержится единиц самого старшего разряда, т.е. сотен. Точно так
же мы говорим о наиболее и наименее значащих разрядах двоичного числа. В числе 10110 наиме¬
нее значащий разряд — это самый правый разряд (0), а наиболее значащий — первый слева (1).
Рассматривая байты, мы поступаем так же : разбиваем 16-разрядное число на два байта (числа по 8
разрядов) — более значащий (старший) и менее значащий (младший). В шестнадцатеричном числе
10A2 старший байт равен 10, младший — A2. Если число десятичное, то выделение байтов произво¬
дится не так просто. Для чисел больше 255 значение старшего байта можно найти из табл. 3.2.
Таблица 3.2. Старший и младший байты чисел
Десятич.
Шестнадца-
Ciарший
Младший
число
терич экв
байт
байт
Дес.
Шест.
Дес
Шест
001
0001
000
00
001
01
255
00FF
000
00
255
FF
256
0100
001
01
000
00
257
0101
001
01
• 001
01
511
01FF
001
01
255
FF
512
0200
002
02
000
00
513
0201
002
02
002
01
767
02FF
002
02
255
FF
768
0300
003
03
000
00
769
0301
003
03
001
01
Число 255, или FF, — это наибольшее число, которое может находиться в младшем байте. Если
число больше 255, то в старший байт нужно добавить 1. Поэтому число 256 имеет 1 в старшем бай¬
те и 0 — в младшем. Теперь заметим, что число 512, которое получается добавлением еще одной 1
38
к старшему байту, в 2 раза больше 256, а 768 в 3 раза больше 256. Другими словами, старший байт
увеличивается на 1 каждый раз, когда число увеличивается на 256.
Теперьоассмотрим два слова C@ и С! для операций с отдельными байтами. Слово C@ похоже
на слово @, но оно извлекает значение только одного байта (буква С в имени происходит от слова
character, т.е. символ, потому что любой символ ASCII представляется одним байтом). После C@
старший байт в стеке должен быть равен 0. Понимаете ли вы, почему?
Слово С! подобно !, но оно производит запись только младшего байта числа, находящегося в сте¬
ке. Оно обычно используется, если число в стеке не превосходит 256. Проделаем несколько экспе¬
риментов. Слово PAD (буфер, записная книжка) выдает в стек адрес в памяти, который является
началом буфера, где пользователь может хранить свои данные. Положите в стек число 257 и зане¬
сите его в PAD с помощьк)
257 PAD
Теперь давайте посмотрим, что окажется в каждом байте по адресу PAD и PAD+1, для чего вве¬
дем
PAD C@ . PAD 1 + C@ .
Вот что вы увидите :
1 1 ok
Теперь повторите то же самое с числами 255 и 256. Потом попробуйте числа из табл. 3.2. Вы бу¬
дете видеть раздельно младший и старший байты вводимых чисел. Понятно ли вам, почему? Вы за¬
писываете в PAD число целиком с помощью !, но извлекаете его в стек побайтно и поэтому видите
оба байта. Порядок следования байтов может меняться от компьютера к компьютеру, в зависимости
от того, как хранятся старший и младший байты числа в памяти. В большинстве случаев старший
байт числа хранится в более старшем адресе. Дальнейшее рассмотрение операций над байтами про¬
должим в упражнениях.
Упражнения
1. Определите слово .LSB для печати младшего байта числа, находящегося в стеке. Не используйте PAD и C@. (Подсказ¬
ка: какое значение старших восьми разрядов должно иметь число в стеке перед операцией .? Как установить эти разряды в
0?.)
2. Определите слово .MSB для печати старшего байта положительного числа, находящегося в стеке. (Совет: вспомните,
что к старшему байту числа добавляется 1 каждый раз, когда к нему прибавляется 256, и что оператор / производит округ¬
ление с уменьшением.)
3. Переопределите .LSB, пользуясь методом определения .MSB. (Указание : вы должны применить DUP к числу, находя¬
щемуся в стеке, и, кроме того, SWAP и — .>
4. Дайте новое определение .LSB и .MSB под именами .LSB2 и« .MSB2, используя PAD и C@. Очень часто одно и то же
можно на Форте сделать разными способами. Наилучший метод — это обычно компромисс между требуемыми ресурсами
памяти и временем исполнения. Какое из определений .LSB и .MSB будет самым быстрым?
5. Напишите слово для деления числа без знака, находящегося на вершине стека, на 256, не используя для этого никаких
чисел.
6. Напишите слово для добавления числа 256 к числу на вершине стека путем прибавления 1 к чему-то.
Американский стандартный код для обмена информацией (ASCII)
Кроме представления чисел разряды в памяти могут представлять алфавитно-цифровые символы.
Символы просто кодируются числами, при этом наиболее распространенным является код ASCII,
полное наименование которого приведено в заголовке. Первоначально код ASCII предназначался не
для компьютеров, а для телекоммуникаций. Это привело к тому, что некоторые коды имеют стран¬
ные названия. Например, код 7 (называемый BEL — звонок) предназначался для звонка в момент
возврата каретки с целью привлечения внимания оператора. Код 5 называется WRU (от Who aRe
yoU — кто там?), предназначался для запроса служебного позывного оператора на другом конце
линии, код 25 — ЕМ (End of Media — конец носителя) может быть использован для индикации
конца телеграфной ленты. Хотя мы еще увидим в гл. 9, как Форт обращается со строками симво¬
лов, сейчас мы рассмотрим, как он воспринимает отдельные символы. Несмотря на то, что код
ASCII не предназначался для ЭВМ, символы в нем все же упорядочены логично с точки зрения
ЭВМ, т.е. в порядке расположения букв алфавита. Полный набор кодов ASCII приведен в приложе¬
нии Г. В табл. 3.3 показано только, как он организован.
39
Таблица 3.3. Сводка кодов ASCII в десятичной и двоичной форме
От
До
Десятич¬
ный
Двоичный
Десятич- Двоичный
ный
Назначение
000
00000000
031
00011111
Коды управления ЭВМ
032
00100000
064
01000000
Цифры и знаки пунктуации
065
01000001
090
01011010
Буквы 'A-Z
091
01011011
096
01100000
Разные знаки
097
01100001
122
01111010
Буквы a-z
123
01111011
127
01111111
Разные символы
128
10000000
255
11111111
Кодами ASCII не яиляююя
Вы можстс заметить, что в стандартном коде фактически используется только 7 разрядов из бай¬
та. Это объясняется тем, что для телетайпов использовалась семидорожечная бумажная лента с
семью отверстиями поперек ленты. (Она же использовалась для хранения программ и данных до
сравнительно недавнего времени.) Числа с восьмым разрядом, установленным 6 1 (128 — 255), не
являются кодами ASCII как в Форт-79, так и в Форт-83, но в некоторых версиях Форт и большин¬
стве микрокомпьютеров частично используются. Например, в MMSFORTH с ЭВМ TRS-80 и IBM
PC они используются для графических и специальных символов. Первые 31 код называются управ¬
ляющими кодами. Хотя их назначение стандартизовано для передачи данных, но в микрокомпьюте¬
рах они применяются для разных целей, т.е. стандарт нарушается. Тем не менее некоторые из них
все же стандартизованы, как, например, 8 — код возврата на позицию влево, 13 — возврат карет¬
ки, 10 — перевод строки. Управляющие коды посылаются с клавиатуры путем нажатия клавиши
’’Control” (управление, иногда обозначается ”Ctrl”) и одновременно какой-то литерной клавиши,
например для возврата влево на позицию — это Ctrl-H. Если на клавиатуре нет клавиши ”Ctrl”, то
в вашей версии Форта должна быть предусмотрена какая-либо другая клавиша, которая действует
как ”CtrF\ например в MMSFORTH с ранними моделями ЭВМ TRS-80 использовалась клавиша
’’Clear” (очистка). Вам нужно посмотреть по своей документации, какие управляющие коды ис¬
пользуются в вашей версии Форта и какую клавишу нужно использовать, если у вас нет клавиши
”Ctrl”. Чтобы узнать, как в вашей версии Форта используются коды ASCII, воспользуйтесь двумя
словами EMIT и KEY. Введите
6Ь EMIT
и на экрйне должно появиться
A ok
Слово EMIT берет число с вершины стека и посылает его ASCII-эквивалент на экран. Испытайте
со словом EMIT другие числа, сравнивая получаемые результаты с таблицей в приложении Г, что¬
бы получше прочувствовать, как ваше оборудование обращается с кодами ASCII. Некоторые особые
символы, такие к^к тильда ”~”, ’’стрелка вверх” О, квадратные скобки ([]) на различных компью¬
терах могут выглядеть по-разному, и то, что печатает ваш принтер, также может отличаться от то¬
го, что выводится на экране. (Предупреждение: использование чисел меньше 32 может привести к
нержиданным результатам, так как это управляющие коды.)
Если слово EMIT позволяет вывести символы ASCII, соответствующие числу, находящемуся в
стеке, то слово KEY позволяет выдать в стек код символа ASCII, вводимого с клавиатуры. Введите
слово
KEY <BK>
Ничего заметного при этом не случится, даже сообщение ”ок” не появцуся. Теперь нажмите кла¬
вишу ”В”, а потом . ; после этого вы увидите, что в стек было положено число 66. Слово KEY при¬
казало Форту приостановить то, что он делал^и подождать, когда'будет нажата какая-либо клави¬
ша, тогда код, соответствующий символу клавиши, кладется в стек. Когда вы нажали клавишу ”В”,
вы поместили значение кода ASCII в стек и увидели этот код, когда ввели . (точку). Что вы увиди¬
те, если введете с клавиатуры
KEY EMIT <BK>
40
Попробуйте после этого нажать какую-либо клавишу. Можете ли вы объяснить результат? Если у
вас нёт компьютера под рукой, то вот, что вы должны увидеть, если, скажем, нажмете Z после
<BK> :
Z ok
Полезно уметь делать преобразования кодов ASCII, например, для представления вместо букв
нижнего регистра букв верхнего регистра и наоборот. Посмотрим, как это делается в упражнениях.
Упражнения
Наиболее удобно (и быстро) коды ASCII преобразуются с помощью операций над отдельными битами, с которыми вы уже
знакомы. Обратитесь к табл. 2.4 и приложению Г для справки о значениях разрядов, которые нужно преобразовать в приво¬
димых заданиях.
1. Напишите слово, которое меняет любой код больше 127 на код меньше 127, но не влияет на код меньше 127. (Совет:
измените один разряд из "Iй в ”0".)
2. Напишите слово для преобразования любого управляющего кода в печатный символ. Сделайте это двумя способами: с
помощью поразрядных операций и с помощью сложения.
3. Напишите слово для преобразования символов нижнего регистра в символы верхнего регистра.
4. Напишите слово, которое делает обратное преобразова¬
ние.
5. Напишите слово, которое позволяет выдать в стек адрес памяти, а затем печатает символ ASCII, который находится по
этому адресу.
6. Напишите слово, которое снимает символ с клавиатуры и помещает его по адресу, который выдает в стек слово PAD.
Выводы
Теперь вы должны хорошо представлять себе, как хранятся числа в памяти, что такое двоичные,
десятичные и шестнадцатеричные числа, как пользоваться словом BASE, чтобы посмотреть числа в
различных системах счисления и как обращаться с отдельными разрядами чисел.* Если вы разобра¬
лись в этих вопросах достаточно хорошо, то вы уже знаете больше о работе компьютера, чем мно¬
гие программирующие на Бейсике, Фортране и Паскале. Мы изложили эти вопросы в начале, что¬
бы заложить прочный фундамент. Но никто, кроме программиста, который постоянно работает на
уровне битов и байтов, не может точно сказать, что делают операторы типа XOR и AND. Почти все
мы вынуждены заглянуть в таблицу или ввести что-то с клавиатуры, чтобы вспомнить, чему равен
шестнадцатеричный эквивалент числа 245, а большинство из нас не смогли бы даже вспомнить, как
хранится знак числа, если бы не было этого краткого обзора. Важно то, что если вы однажды поня¬
ли этот материал, то вам будет легко вспомнить его, если потребуется. Поэтому не пытайтесь за¬
помнить все детали этой главы. Вы можете подумать, что, хотя вы поняли представление алфавит¬
но-цифровой информации кодами ASCII, вам это не принесло практической пользы. Вы правы., Но
уже скоро мы узнаем о том, что возможности Форта в обработке символьной информации очень ве¬
лики.
Наконец, в этом месте вы можете задать вопрос: должны ли мы работать только с целыми числа¬
ми? Сам по себе Форт, определенный в соответствии со стандартами, не применяет арифметики с
плавающей запятой (т.е. арифметики, которая учитывает положение десятичной запятой в числах).
Некоторые считают, что целочисленной арифметики вполне достаточно, но для того, кто занимает¬
ся решением научных и технических задач, ясно, что это бессмыслица. Хотя действия с целыми
числами выполняются быстрее, да и в других языках программирования использованием чисел с
плавающей запятой иногда чрезмерно увлекаются, все же бывает, что целые числа при решении
практических задач оказываются неудобными. В главе 4 мы узнаем, как работать с числами спла-
вающей запятой. И, кроме, того узнаем значительно больше о других мощных арифметических
операциях в языке Форт.
Глава 4
ЕЩЕ ОБ АРИФМЕТИЧЕСКИХ ОПЕРАЦИЯХ
Мы уже не раз говорили о том, что компьютеры используются не только для операций с числами, но и во многих других
областях, однако, что бы они ни делали, они имеют дело с числами. Эта большая глава представляет собой что-то вроде по¬
пурри на тему чисел. Однако если в предыдущих и большинстве следующих глав для любой серьезной работы по програм¬
мированию был нужен весь материал, то некоторые части этой главы потребуются вам только в том случае, если вы будете
заниматься **перемалыванием” чисел, например числовыми базами данных. Как минимум, вы должны изучить данное введе¬
ние и разделы "Операторы для работы с небольшими числами”, **Некоторые проблемы операции деления**, **Операции с ве-
личинами и знаками чисел”. Если вы работаете в стандарте Форт-83, вам нужно также прочитать раздел **Деление с округ¬
лением, деление с отрицательными числами**. Если вы будете работать с очень большими числами и дробями, вам нужно
изучить разделы **Почему используются целые числа?**, **Масштабирование чисел**, **Числа двойной длины** и **Совместное
применение чисел одинарной и двойной длиньГ. А если вас интересует работа с числами с плавающей запятой, вы должны
прочитать последний раздел. Если вы совершенно устали от арифметики, переходите к гл. 5 или даже гл. 6; для их понима¬
ния вам достаточно того, что вы уже узйали, но потом вам все равно придется вернуться, а потому наш совет — не пропу¬
скайте материал этой главы. Наконец, чтобы считать себя всесторонне образованным программистом на языке Форт, вам по¬
требуется узнать все, что изложено в этой главе.
К настоящему времени вы изучили четыре арифметические операции: сложение, вычитание, умножение и деление и со¬
ответствующие слова Форта +, -, * и /. Нам остается мало что добавить, кроме как упомянуть о потенциально возможной
проблеме арифметического переполнения. Предположим, вы складываете числа 36000 и 37000. Результат должен равняться
73000, т.е. быть больше, чем 16-разрядное число, которое может быть в стеке. Это и есть переполнение, которое приводит к
неверному результату. В большинстве версий Форта в результате сложения 36000 и 37000 будет получаться 7464, что, оче¬
видно, неверно. Переполнение может возникнуть также при операциях * и -. В то время как большинство языков обнаружат
ошибку переполнения и либо прекратят исполнение программы, либо выдадут сообщение об ошибке, Форт этого не сделает.
Почему? Потому что проверка на наличие ошибок занимает время ЭВМ, и обычно немалое, поэтому, чтобы не снижать ско¬
рости работы, заботу об избежании ошибок Форт предоставляет программисту. Вторая причина — желание, чтобы програм¬
ма работала, даже если произошла ошибка. Нас интересует проверка возникновения переполнения и его предотвращение.
Как быть, если вы хотите работать с числами больше 32767 или меньшими -32767, т.е. с числами больше 65535, если не
учитывать знака. Имеется два способа решения этой проблемы. Один из них — это использование так называемых чисел
двойной длины (или, как иногда говорят, двойной точности или просто двойных чисел), которые представляются 32 разряда¬
ми. Другой — в использовании чисел с плавающей запятой. Обе эти возможности мы рассмотрим позже в этой главе.
Операторы для работы c небольшими числами
В стандартах описываются несколько специальных операций для работы с небольшими числами.
Это 1+, 1-, 2+ и 2-. В стандарте Форт-83 есть еще операция 2/, а в большинстве версий Форта — и
другие. Например, в MMSFORTH есть такие операции: 2*, 8*, 16* и 64*. Эти операции выполняют
то, что и следует ожиддать, работая с числами, находящимися на вершине стека, т.е. производят
умножение, деление, сложение и вычитание с числом, которое входит в имя слова. Зачем нужны
эти операции? Почему бы не использовать два слова 2 + вместо специального слова 2+? Для ускоре¬
ния операции. Дело в том, что при использовании специального слова, определенного на машинном
коде, операция выполняется значительно быстрее, чем при использовании двух слов 2 +. Например,
чтобы разделить число на 2, требуется каждый разряд числа сдвинуть на одну позицию вправо, что
делается в машинном коде очень быстро. На IBM PC на выполнение 1 миллиона операций 2/
MMSFORTH расходует 22 с, а на миллион операций 2 / 80 с. Поскольку действия с числами 1 и 2
встречаются очень часто, особенно при повторяющихся операциях, использование этих слов может
значительно увеличить скорость работы программ. Может быть, это и мелочь, однако это хороший
пример того, как Форт оптимизирует быстродействие.
Некоторые проблемы операции деления
Предположим, что вы хотите умножить число 20000 на 5 и разделить произведение на 2, при
этом должно получиться 50000. Попробуйте это сделать, введя
20000 5 * 2 / U.
42
и то, что вы увидите, будет наверняка неправильно. При умножении происходит переполнение и
заведомо неверный результат умножения точно делится на 2. Но эту задачу можно решить иначе:
20000 2 / 5 *
ведь не всегда же вы сможете узнать, будут ли расположены большие и малые числа в правильной
последовательности.
Слово */ разрешает эту проблему, запоминая промежуточный результат в виде 32-разрядного
числа, а не 16-разрядного, как обычно, допуская значение произведения до 4271406735. Таким об¬
разом устраняется возможность переполнения.
Еще одна проблема, возникающая при делении, — это проблема остатка. При целочисленных
операциях сложения, вычитания и умножения результат представляет собой другое целое число.
Для деления это не так. Например, если 3 поделить на 2, должно получиться 1.5 = 1 + 5/10. Нам
нужно каким-либо образом определить остаток от деления (который иногда называется модулем де¬
ления). Остаток находится с помощью слова MOD. Если вы введете
3 2 M0D.
то на экране получите 1.
25 7 M0D.
дает в результате 4. Другими словами, слово MOD выдает остаток от деления двух чисел, или мо¬
дуль. Есть еще два других слова, которые дают в результате остаток. Одно из них, /MOD, дает ос¬
таток от деления двух чисел и частное, которое помещается поверх остатка. Таким образом,
5 3 /M0D..
дает в результате пару чисел 1 и 2, в то время как
25 7 /M0D..
выдает в результате 3 4.
Слово */MOD связано с обеими операциями */ и MOD.
3 3 2 */M0D..
приводит к результату 4 1. Второе и третье число в стеке (в данном случае 3 и 3) перемножаются,
образуя 32-разрядный результат, чтобы избежать переполнения, затем делится на число, находяще¬
еся на вершине стека, остаток от деления остается в стеке, поверх него на вершине стека находится
частное. Назначение слова */MOD такое же, как и */, т.е. избежать переполнения во время опера¬
ции умножения.
Деление с округлением, деление с отрицательными числами
В школе вас учили, что, если при делении двух чисел возникает остаток, результат нужно округ¬
лить до ближайшего целого в сторону уменьшения (вниз), т.е. 3/2 дает в частном 1 и в остатке 1.
Ну а что делать, если либо делимое, либо делитель отрицательные? По интуиции вы, возможно,
считаете, что, например, при делении -6/4 частное будет -1, а какой будет остаток? Вы, если хоти¬
те, можете умножить частное на делитель и сложить с остатком, чтобы получить делимое. Таким
образом, в данном случае остаток будет равен -2. Потому что -2 + -1 x 4 равно -6. Именно так уст¬
роено деление в Форт-79. Для подтверждения проверим на Форт-79:
-6 4 /.
при этом получается -1, в результате операций
-6 4 M0D.
получается -2 и после
-6 4 /M0D..
получается -1 -2. Для стандарта Форт-79 можно сформулировать правила ’’интуитивного” деления:
1) если либо делитель, либо делимое, но не одновременно отрицательные, то частное также от¬
рицательное; 2) частное независимо от знака округляется в сторону, ближайшую к 0; 3) оста¬
ток принимает знак делимого, или второго числа в стеке.
Пользуясь этими правилами, мы всегда получаем результаты, приведенные в последних приме¬
рах. Эти правила справедливы для большинства версий Форта, за исключением тех, которые осно¬
ваны на стандарте Форт-83. Точное значение результата операции -6/4 равно -1.5. Согласно
школьным правилам надо округлить это число в сторону уменьшения, и мы получили бы -2 (-2
меньше, чем -1). Это так называемое деление с округлением по нижней границе (по ”полу”). Его
суть в том, что за частное от деления принимается ближайшее меньшее значение. Частное всегда
округляется до нижней границы. А как определить остаток? Вы снова можете умножить частное на
делитель и добавить остаток, чтобы получить исходное число. Частное равно -2, делитель 4, их
43
произведение равно -8. Чтобы получить -6 после прибавления к этому произведению остатка, он
должен быть равен 2. Сформулируем правила для деления с округлением по нижней границе. 1) ес¬
ли либо делитель, либо делимое, но не дба сразу отрицательные, то частное также отрица¬
тельное (точно так же, как для ’’интуитивного” деления); 2) частное независимо от его знака ок¬
ругляется в сторону ближайшего меньшего числа; 3) остаток от деления принимает знак дели¬
теля (в противоположность ’’интуитивному” делению).
В Форт-83 применяется деление с округлением по нижней границе, что является полной проти¬
воположностью Форт-79, в связи с чем нужно быть внимательным при переносе программ из одного
стандарта в другой. Хотя большинство машинных языков программирования работают как Форт-79
(за исключением АПЛ), с математической точки зрения более корректно округление по нижней
границе, т.е. независимо от знака в сторону нижней границы. Тем не менее многие считают, что
это противоречит здравому смыслу. Если вас это смущает, может быть, вам помогут несколько при¬
меров, приведенных ниже. Обычное деление на 0 приводит к бесконечному результату, т.е. к раз¬
рыву в нуле. Это может привести к сложностям в графике и других применениях, например для ро¬
бототехники, где необходимо обеспечить плавный переход от положительных к отрицательным чис¬
лам. Деление с округлением по нижней границе в связи с этим предпочтительнее, но нужно иметь
в виду, что оно занимает времени немного больше.
Обычное деление
("интуитивное”)
Деление с округлением
по нижней границе
4 2 /
2 Ok
4 2 /
. 2 ok
3 2 /
1 ok
3 2 /
. 1 ok
2 2 /
1 ok
2 2 /
. 1 ok
1 2 /
0 ok
1 2 /
. 0 ok
0 2 /
0 ok
0 2 /
. 0 ok
-1 2 /
0 ok
-1 2 /
. -1 ok
-2 2 /
-1 ok
-2 2 /
.-1 ok
-3 2 /
-1 ok
-3 2 /
.-2 ok
-4 2 /
-2 ok
-4 2 /
.-2 ok
В связи с необходимостью преобразования программ, написанных на Форт-79, в стандарт Форт-
83 нужно иметь правила или алгоритм преобразования результата обычного деления в результат
деления с округлением по нижней границе. Проще всего представить этот алгоритм, определив два
слова на языке Форт. На Форт-79 слово
: FL/ /MOD SWAP IF DUP 0 < IF 1- THEN THEN ;
будет выдавать частное с округлением по нижней границе при делении чисел в соответствии со
стандартом Форт-79. Вам еще не приходилось обращаться с некоторыми словами, использованными
в данном определении, но их нетрудно понять. Выражение /MOD SWAP выдает частное и остаток
и переставляет остаток на вершину. Если слово IF обнаруживает положительное число, т.е. остаток
не равен 0, исполняются слова, которые следуют за IF до второго оператора THEN. В этом случае
частное дублируется на вершине стека и сравнивается с нулем с помощью оператора <, который
возвращает 1, если частное отрицательное. Если это так, то следующее слово IF разрешает вычесть
1 из частного, в противном случае слово THEN передает исполнение следующему слову THEN, за¬
канчивающему определение слова. Иначе говоря, если остаток не равен 0 и частное отрицательное,
то из него вычитается 1. Поэтому правило преобразования результата обычного деления в резуль¬
тат с округлением по нижней границе формулируется так: Если частное отрицательное, а оста¬
ток не равен нулю, нужно уменьшить частное на 1. Остаток от деления двух чисел с округлением
по нижней границе получается на Форт-79 с помощью слова
• FLM0D OVER OVER / 0 < IF OVER OVER FL/ * -
ELSE MOD THEN ;
Операторы OVER OVER делают копию двух чисел (вместо них можно применить слово 2DUP,
что будет еще быстрее), и если частное отрицательное, то исполняются слова между IF и ELSE. В
этом случае два числа еще раз копируются, производится их деление с помощью ранее определен¬
ного слова FL/, частное умножается на делитель и произведение вычитается из делимого. Если час¬
тное не отрицательное, то исполняется слово, следующее после слова ELSE, т.е. определяется
’’обычный” остаток. Вот алгоритм, по которому работает слово FLMOD: если частное от деления
отрицательное, то остаток с округлением по нижней границе находится умножением частного
на делитель и последующим вычитанием произведения из делимого. На самом деле этот алгоритм
44
применим как к отрицательному, так и к положительному частному. Мы сформулировали его так
только в учебных целях. Слово FLMOD так же эффективно можно определить иначе:
: FLMOD 2DUP FL / * - ;
Освоив стековые манипуляции в гл. 2, вы можете заметить, что частное и остаток деления с ок¬
руглением по нижней границе можно найти по аналогии со словом /MOD с помощью
: FL/MOD 2DUP FLMOD ROT ROT FL/ ;
Следует подчеркнуть, что данные определения работают очень медленно по сравнению с теми
определениями на языке ассемблера, которые введены в Форт-83. И ими нужно пользоваться толь¬
ко в случае крайней необходимости.
Упражнения
1. Проделайте в уме следующие примеры, используя компьютер только для проверки ответов. Найдите остаток и частное
при ’’обычном” делении и делении с округлением по нижней границе:
(а) 10/2 (б) 0/2
(в) 11/3 (г) -11/3
(А) 11/-3 (e) -11/-3
(ж) -10/2 (з) 10/-2
(и) -10/-2 (к) -10/0
2. Имеется текст из нескольких сотем литер, расположенный в строчках по 50 символов в строке. Напишите слово для
вычисления номера строки, в которой находится 112-я литера. (Не забудьте, что вы работаете в целочисленной арифмети¬
ке.)
3. Используя слово MOD, определите слово для вычисления количества символов, предшествующих 112-му символу в той
строке, где он находится.
4. Используйте идеи из упражнений 2 и 3 и напишите слово для определения номера строки и номера в строке любого
символа в тексте из строчек фиксированной длины. При вызове слова в стеке должны находиться номер символа и длина
строки.
Приемы, которые вы применили в упражнениях 2-4, пригодятся впоследствии, когда вам потребуется определить место¬
нахождение данных, хранящихся в блоках Форта, а именно эти приемы помогут вам найти номер блока, в котором находит¬
ся конкретный фрагмент данных и число байтов смещения внутри этого блока.
5. Напишите определение слова MOD (под именем NEWMOD) для Форт-79, используя стандарт Форт-83. В этом про¬
цессе вам нужно будет дать определение деления / в Форт-79.
6. Найдите значение выражения 2000*100/30. Проделайте это двумя способами, избегая в обоих случаях переполнения. В
первом используйте */, в другом — /MOD. Теперь проделайте то же, чтобы в результате выдавалось значение частного и ос¬
татка. Можете ли вы сделать это также двумя способами?
Дальше мы узнаем, как использовать остаток, который получается со словом MOD для выполнения арифметических опе¬
раций с имитацией плавающей запятой.
Операции с величинами и знаками чисел
Каждое из слов МАХ и MIN сравнивают два числа и оставляют в стеке соответственно большее
или меньшее из них. Так, например,
7 3 МАХ
оставляет в стеке 7, в то время как
7 3 MIN
возвращает в стек 3. Имеется много применений этих слов, некоторые из них мы увидим в упраж¬
нениях. Одно из наиболее частых применений — для ограничения диапазона чисел, поступающих
на входе. Предположим, что вы хотите ограничить диапазон чисел значениями от 5 до 25. Это мо¬
жет быть сделано следующим словом:
: LIMIT-RANGE 5 МАХ 25 MIN ;
Если встречается число больше, 25, то в стек помещается число 25, в то же время, когда число
меньше 5, в стек помещается 5. При определении слов FL/ и FLMOD вы уже видели, как можно
сравнить числа, и использовали для этого конструкцию IF...THEN, и вам, вероятно, нетрудно пред¬
ставить, что МАХ и MIN можно определить таким же способом. Однако такое определение будет
очень медленным. Многие программисты стремятся чаще использовать конструкцию IF...THEN, из¬
бегая применения слов МАХ и MIN. Но это их ошибка, поскольку МАХ и MIN позволяют значи¬
тельно ускорить исполнение программы.
45
Слова ABS и NEGATE воздействуют на знак чисел. (Со словом NEGATE вы встречались в гл.
3.) Слово ABS возвращает в стек абсолютную величину числа. Так, например,
-5 ABS
возвращает в стек 5, а
5 ABS
выдает в стек то же самое число 5.
В свою очередь, слово NEGATE всегда изменяет знак числа.
Т.е.
-5 NEGATE
положит в стек 5, в то время как
5 NEGATE
вернет в стек -5. Более медленная версия слова ABS, основанная на применении NEGATE и конст¬
рукции IF...THEN, приводится ниже:
: ABS DUP 0 < IF NEGATE THEN ;
Покажем применение этих операторов на нескольких упражнениях.
Упражнения
1. Определите слово, которое будет печатать наибольшее из трех верхних чисел в стеке.
2. Определите слово, которое будет печатать наименьшее из трех верхних чисел в стеке, оставляя в стеке исходные числа.
3. Определите слово, которое будет выдавать в стек 1, если любое из трех верхних чисел больше 5 (используйте
оператор >).
4. Определите слово, которое будет возвращать 1, если все три верхних числа в стеке больше 5 (также используйте
один из операторов сравнения).
5. Опишите слово, которое будет возвращать 1, если число на вершине стека больше, чем два следующих, находящихся
ниже.
6. Определите слово, которое будет выражать разность температур, два значения которых находятся в стеке, как положи¬
тельное число, независимо от знака температуры.
7. Определите слово, которое будет находить наибольшую абсолютную величину двух чисел, независимо от того, положи¬
тельные они или отрицательные.
8. Определите слово, которое будет выдавать абсолютное значение того из двух чисел, которое ближе к нулю, независимо
от знака чисел.
9. Используйте слово NEGATE для определения слова, действующего противоположно слову ABS, т.е. возвращающего
число, равное по абсолютному значению исходному числу, но всегда отрицательное или нулевое.
10. Определите слово OTHER-QUAD, которое переводит координаты точки (x,y) в прямоугольной системе координат в
противоположный квадрант, сохраняя абсолютное значение x и у. Это значит, что нужно перевести точку из верхнего левого
угла в правый нижний, из нижнего левого — в верхний правый и т.д. Например, -23 5 OTHER-QUAD должно в результате
выдать 23 -5.
11. Определите слово NEWNEGATE с функцией NEGATE, используя только число и оператор *.
Определение математических функций
Одной из приятных возможностей языка Форт является то, что он позволяет определять матема¬
тические функции, расширяя язык для математических приложений. В качестве очень простого
примера вы уже встречались с определением слова для вычисления квадрата числа:
: SQUARE DUP * ;
Для'обозначения названий функций удобно, хотя это не общепринято, пользоваться алгебраиче¬
ской записью, заменяя пробелы точками. Например, определение слова для вычисления выражения
а2+Ь2 может быть
: A2.+.B2 DUP * SWAP DUP * + ;
или для выражения a(a + b)
: A(A.+.B) (b а — aa+ab) DUP ROT + * ;
Для расчета значения сложных выражений лучше всего разбить их на максимально возможное
число простых выражений. При этом будет проще следить за состоянием стека, давая определения
отдельным словам, и вообще, определения слов должны быть как можно короче и быстрее. Предпо¬
ложим, например, что b лежит в стеке вторым, число а находится на вершине стека и вы хотите
найти значение выражения a^ab. Можно определить слово:
46
: A2.+ AB DUP DUP * ROT * + ;
которое сначала вычисляет значение а2, потом ab и затем складывает оба произведения. С другой
стороны, выражение может быть разложено на множители a(a + b), а это выражение, как мы толь¬
ко что видели, может быть вычислено с помощью
• A(A +.В) DUP ROT + * .
Очевидно, что второй вариант проще, короче и более быстродействующий.
Упражнения
1. Объем пирамиды вычисляется по формуле Ah/2, где А — площадь основания пирамиды, h — высота пирамиды.Орре-
делите слово PYRVOL (объем пирамиды) для вычисления значения функции с округлением результата до ближайшего цело¬
го числа.
2. Определите слово F->C для пересчета градусов Фаренгейта в градусы Цельсия по формуле С - 5(F — 32)/9. Значения
входных и выходных величин должны быть округлены до целых значений. Можете ли вы предложить способ получения зна¬
чений с точностью до десятых долей градуса? Для этого применяется так называемое масштабирование, о котором мы вско¬
ре расскажем.
3. Напишите слова для вычисления следующих функций, применяя, где возможно, разложение на множители. Дайте ва¬
шим словам подходящие названия.
(а) а/с + b/c (6) а/с + b/c2
(в) a2+2ab+b2- q (г) 3a2+6ab+3b2
(д) a4+4a3b+6a2b2+4ab3+b4
Почему используются целые числа?
Из того, что вы уже знаете, ясно, что Форт сталкивается с определенными проблемами при рабо¬
те с очень большими числами и числами с плавающей запятой (такими, например, как
23.497 или -0.96).
Некоторые считают, что использования 16-разрядных и 32-разрядных целых чисел достаточно
практически для всех применений микро- и мини-ЭВМ и работа с числами с плавающей запятой
приводит к расточению времени и памяти ЭВМ. Другие, включая авторов, кто использует ЭВМ для
научных и технических задач, считают, что, хотя в большинстве применений можно обойтись це¬
лыми числами, числа с плавающей запятой очень нужны для практического применения языка
Форт в некоторых классах задач. Несмотря на отсутствие требований в стандартах Форт-79 и Форт-
83, в некоторых версиях языка поддерживается в той или иной мере арифметика с числами с пла¬
вающей запятой. Мы рассмотрим более детально математические основы применения чисел с пла¬
вающей запятой в конце этой главы. Но для этого, а также для того, чтобы понять,как извлечь
максимум возможностей из использования целых чисел, мы должны сначала рассмотреть, что пред¬
ставляют собой числа с плавающей запятой.
Замечания о числах с плавающей запятой1
Рассмотрим число 1298. Оно может быть представлено следующим образом:
1000 + 200 +' 90 + 8
Число 1298.325 можно представить так :
1000 + 200 + 90 + 8 + 0 3 + 0 02 + 0 005.
Если в числе имеется десятичная запятая, это означает, что часть его, которая стоит после деся¬
тичной запятой, представляет сумму дробей, каждая из которых может быть выражена в виде отно¬
шения, которое в десятичной системе имеет в знаменателе 10, 100 и т.д. Таким образом можно
представить 0.325 как
3/10 + 2/100 + 5/1000,
или, приведя к общему знаменателю, как
300/1000 + 20/1000 + 5/1000 = 325/1000.
Таким образом, любое число с плавающей запятой может быть представлено как целое число
плюс отношение двух других целых чисел. Так как числа двойной длины используют 32 разряда, то
наибольшее представимое целое число равно 4271406735, а с помощью целых чисел могут быть
представлены числа с плавающей запятой не меньше этого числа с погрешностью, не превышающей
1/1000000000. Например, число 4271406735.123456789 можно представить как 4271406735 +
47
123456789/1000000000. Это совсем немалый диапазон и малая погрешность, если бы только имелась
возможность следить за значением целой и дробной части. Форт оставляет решать эту задачу про¬
граммисту. С другой стороны, числа с плавающей запятой называются так потому, что правила
арифметики и программы для калькуляторов или комггьютеров отслеживают положение десятичной
запятой, позволяя ей "плавать”, где нужно, т.е., положение плавающей запятой в числе автомати¬
чески отслеживается машиной.
Число 1 1/2 может быть представлено точно как 1.5. С другой стороны, рассмотрим число 1 1/3,
оно не может быть представлено как 1.3, 1.333 и т.д. в десятичной системе, сколько бы ни было
разрядов (хотя, если работать в троичной системе счисления, то 1 1/3 можно точно представить
числом 1.1). Другими словами, 1 1/3 — это точное число, в то время как представление его десяти¬
чной дробью является приближенным. Приближение может быть достаточно хорошим, но не абсо¬
лютно точным. Если числа известны точно и если они могут быть представлены как целое число
плюс отношение двух целых чисел, то лучше всего было бы все числа представлять через целые.
Но рассмотрим реальный мир. Точным микрометром можно измерить толщину с погрешностью в
лучшем случае 1/1000 см. Для инженера почти все измерения дают приближенный результат.
Большинство констант, которые применяются в науке и технике, нельзя выразить через целые чис¬
ла или отношением целых чисел. Например, нельзя точно представить число Пи. Таким образом,
во многих областях точное представление целых чисел и отношений не имеет практического смыс¬
ла. To же справедливо для представления чисел с плавающей запятой. Имеется и еще одна практи¬
ческая проблема. В технике часто необходимо использовать числа, которые больше или меньше,
чем представимые 16- или 32-разрядными числами. Например, в больших ЭВМ применяются 64-
разрядные числа. Очень большие и очень малые числа можно представить числами с плавающей
запятой с указанием порядка (иногда их называют действительными числами с показательной или
”научной”формой записи), используя не больше 32 разрядов. Они выражаются как действительное
число с плавающей запятой плюс число 10, возведенное в какую-либо степень. Рассмотрим табл.
4.1.
Таблица 4.1. Степени числа 10
Степень
Число
Степень
Число
1 (т. e
101) 10
-1 (т.е.
10"1) 1/10
2
100
-2
1/100
3
1000
-3
1/1000
4
10000
-4
1/10000
Мы видели, что 0.395 может быть выражено как 395/1000, поэтому 0.395 - 395/1000 = 395 x 10'
3. Аналогично мы можем выразить 3950 как 3950 = 0.395 x 104.
Так как многие принтеры не могут напечатать 10-3 или 104, как показано в тексте, в компьютер¬
ной нотации опускается число 10 и указывается только показатель степени, перед которым стоит
буква E (от exponent — показатель степени), т.е. 0.395 будет представлено как 3.95E-3, а 3950 —
как 0.395E4.
Чтобы преобразовать число из показательной формы представления в обычную форму с пла¬
вающей запятой, нужно перенести десятичную запятую влево на значение показателя степени,
если он положительный (добавляя, если нужно, нули), или вправо, если показатель степени от¬
рицательный. Теперь мы можем выразить очень большие и очень малые числа.Имея только 32
разряда, можно отображать числа больше lE-38, т.е. 100000000000000000000000000000000000000, и
меньше lE.-37, или 0.00000000000000000000000000000000000001.
Число, которое предшествует чисду 10 в степени, может быть 6-разрядным (десятичным). Таким
образом, диапазон представления чисел с плавающей запятой lE-38 999999E38. Необходимо ввести
здесь некоторую терминологию. Целое число, предшествующее числу 10 в степени, называется
мантиссой (если вы знакомы с логарифмами, то, наверное, знаете почему; если же не знаете, то
это не так уж важно), а показатель степени называется порядком.
48
Арифметические операции с числами с плавающей запятой в показательной форме
Арифметические действия с числами, представленными в показательной форме, выполняются по
простым правилам. Вам совершенно не нужно их понимать, для того чтобы понять все за и против
математики целых'чисел и чисел с плавающей запятой, но правила простые, и сейчас самое время .
их изучить.
Чтобы сложить два числа или вычесть одно число из другого, нужно привести их к одному по¬
рядку, а.затем сложить или вычесть их мантиссы. Например,
5E-3 - 2E-2 = 5E-3 - 20E-3 = -15E-3 = -1.5E-2
или 12E10 + 21E11 = 12E10 +210E10 = 222E10 = 22.2E11.
Чтобы перемнржить два числа, нужно перемножить их мантиссы и сложить порядки. Напри¬
мер,
5E-3 * 2E-2 = 10E-5 = 1E-4
или 12E10 * 21E11 = 252E21 = 2.52E23.
Чтобы разделить два числа, нужно поделить мантиссу делимого на мантиссу делителя и вы¬
честь порядок делителя из порядка делимого. Например,
5E*-3/2E-2 = 2.5E-1 или 12E10/25E11 = 0.48E-1 = 0.048.
Представление чисел в форме с плавающей запятой и порядком упрощает арифметические опе¬
рации с большими и малыми числами.
Точность и погрешность
Нам осталось рассмотреть еще несколько вопросов, прежде чем вернуться к аргументам за и про¬
тив использования чисел с плавающей запятой или целых чисел. Точность числа с плавающей за¬
пятой выражается числом знаков в записи мантиссы, или, говоря проще, числом десятичных разря¬
дов числа. Так, число 1.23E5 имеет точность 3 знака, 112.35Ё23 — 5 знаков, а 1230 — 4. Но точ¬
ность и погрешность — это не одно и то же. Если вы пользуетесь измерителем, снабженным нониу¬
сом, то вы сможете считать размер с точностью до 1/10 миллиметра. С другой стороны, если вы из¬
меряете размер горошины, то оценить его с точностью до 0.1 миллиметра вы не сможете, так как
горошина не идеально круглая. Вы можете измерить диаметр 4.5 мм, выражая такой записью, что
точность его 2 знака. Вы можете поддаться искушению и добавить еще два нуля, написав 4.500, т.е.
с точностью 4 знака. Но погрещность измерения, вероятно, будет ближе к миллиметру или 1 знаку.
Если вы записываете число как 4.5 или 4.500, вы в лучшем случае обманываете себя или того,
кто просматривает ваши данные, в худшем случае подтасовываете погрешность измерения. Самый
простой способ подорвать доверие к лабораторному отчету о физических или химических измерени¬
ях — это указать большое число знаков погрешности, путая ее с точностью. В чем здесь суть? В
том, что в реальном мире точность числа лучше 4-6 знаков требуется крайне редко. Одна из при¬
чин, почему числа с плавающей запятой так нравятся ученым и инженерам, состоит в том, что из
них ясно видна точность числа по количеству приведенных в записи знаков. Запись 1.23E5 говорит
о том, что, хотя измеренное значение приблизительно равно 123000, его точность +-1000. С другой
стороны, если число представляется как целое 123000, то большинство представителей технических
наук будут считать, что оно известно с точностью 6 знаков. Числа с плавающей запятой и указани¬
ем порядка лучше всего удовлетворяют потребности науки и техники.
Существует одна важная сфера, где требуется гораздо большая точность. Это денежные расчеты.
Бухгалтер никогда не округлит миллион долларов до трех или четырех знаков. В расчетах должны
указываться все доллары и центы. И редко кому потребуется величина порядка числа 10 или 11 да¬
же для выражения бюджета крупных государств (хотя иногда крупные компании испытывают труд¬
ности в программировании расчетов, так как им приходится оперировать значениями больше трил¬
лиона долларов!). Точно так же редко потребуется и порядок меньше -3 (это соответствовало бы де¬
сятым долям цента или пенса или одной тысячной иены) / Поэтому для бухгалтерских расчетов 32-
разрядные числа с плавающей запятой обычно не подходят. Они не обеспечивают требуемой точно¬
сти и в то же время имеют избыточный диапазон представляемых чисел. Программы бухгалтерских
расчетов лучше всего составлять с применением целых чисел, но предусматривая две позиции для
отображения сотых долей.
49
Числа с плавающей запятой: за и против
Многие преимущества чисел с плавающей запятой вам уже должны быть понятны. Основные из
них: 1) при вычислениях с числами с плавающей запятой обеспечивается отслеживание положения
десятичной запятой и величины числа практически без участия программиста; 2) представление чи¬
сел с плавающей запятой с указанием порядка позволяет работать как с очень большими, так и
очень малыми числами; 3) арифметика с плавающей запятой позволяет задавать точность или по¬
грешность с достаточным количеством знаков; 4) представление чисел с плавающей запятой распро¬
странено в мире инженеров и ученых, не склонных доверять представлению результатов измерений
в реальном мире с помощью целых чисел. Хотя последняя причина в первую очередь психологиче¬
ская, она имеет значение для принятия языка большинством практиков.
Многие фо не все) преимущества целочисленной арифметики вам также должны быть понятны:
1) целые числа обеспечивают большую точность при заданном объеме памяти, для 16-разрядных
чисел диапазон -32768 — 32767 или -2147483648 до 2147483647 для 32-разрядных; 2) с помощью
целых чисел можно представить как собственно целые, так и действительные числа, имеющие це¬
лую и дробную части, например 12.55 = 12 + 55/100; 3) с помощью целых 32-разрядных чисел
представляются целые числа и дроби в диапазоне от 1E-10 и более чем 1E+10, при этом с точно¬
стью на 4 знака больше, чем с помощью 32-разрядных чисел с плавающей запятой.
Но наиболее часто выдвигаемый аргумент в пользу целых чисел — это более высокая скорость
работы. Арифметические действия с плавающей запятой производятся компьютером по приведен¬
ным здесь правилам. Из них вытекает, что при арифметических действиях с плавающей запятой
компьютер должен выполнить большее количество операций сложения, вычитания, умножения или
деления, следовательно, для этого требуется в несколько раз больше времени. Поскольку арифмети¬
ческие действия часто выполняются в циклах много раз, скорость исполнения выдвигается на пер¬
вое место. Самый существенны^ аргумент против включения чисел с плавающей запятой в стан¬
дарт состоит в том, что в таком случае приносится в жертву скорость работы. Но насколько серье¬
зен этот аргумент? Посмотрим, как работает Фортран или Бейсик (и многие другие языки). Именно
с учетом всех за и против, которые мы обсуждаем, другие языки дают программисту свободу при¬
нимать решение, обращаться ли с числами как с целыми илй как с числами с плавающей запятой.
В Форте это также возможно, правда, не во всех версиях.
Возможность пользоваться числами с плавающей запятой должна быть решающим критерием при
выборе подходящей версии Форта. И решение этого вопроса возлагается на программиста..
Для некоторых микрокомпьютеров вопрос об использовании чисел с плавающей запятой стано¬
вится еще более критичным. Как мы увидим дальше, в некоторых компьютерах доступно так назы¬
ваемое сопроцессорное арифметическое устройство, которое практически представляет собой еще
один компьютер, предназначенный для выполненния операций над числами с плавающей запятой,
причем с очень высоким быстродействием, повышенной точностью и в расширенном диапазоне чи¬
сел. Наиболее известным примером может служить сопроцессор фирмы Intel типа 8087, который
может быть использован в IBM PC и совместимых с ней ЭВМ. Микросхема 8087 представляет числа
80-разрядами, при этом мантисса числа может иметь до 18 знаков, а порядок от -4600 до 4600 (в
буквальнОм смысле можно представлять числа больше, чем оценка числа электронов во вселенной).
По причине того, что 8087 использует стек так же, как Форт, ее стек можно частично использовать
для операций с плавающей запятой. Поэтому Форт с сопроцессором 8087 может выполнять некото¬
рые операции с плавающей запятой с потрясающими точностью и диапазоном и значительно быст¬
рее, чем ЦПУ с 16-разрядными числами. Учитывая, что ЭВМ с сопроцессором 8087 могут также ра¬
ботать с памятью объемом до 1 Мбайта, аргументы в пользу применения целых чисел для экономии
времени и памяти, а также получения большей точности становятся слишком слабыми. Имеется на¬
дежда, что в будущем появятся версии языка Форт, в стандарт которых будут включены операторы
для работы с числами с плавающей запятой. Ну а как быть, если ваша версия Форта не может ра¬
ботать в арифметике с йлавающей запятой или вы не можете пожертвовать скоростью работы? Как
можно обращаться с числами, которые большинство из нас записывают как действительные числа
(с десятичной запятой)? Как вы будете обращаться с дробями? Решать эти вопросы вам поможет
масштабирование.
50
Масштабирование чисел
Слово ’’масштабирование” применяется в том же смысле, что и в технике, когда делается чер¬
теж, который отображает объект в увеличенном виде. Предположим, что вам нужно рассмотреть
синьку часового винта. Если винт имеет длину всего 1 мм, его нужно изобразить в каком-то масш¬
табе, например 100:1. Это значит, что чертеж должен быть выполнен в масштабе, в 100 раз превы¬
шающем истинные размеры. Подобным образом карта может быть масштабирована в обратную сто¬
рону, скажем, 1 см вместо 1 км, т.е. 1:100000.
Проще всего показать масштабирование на примере. Предположим, вы хотите сложить в столбик
доходы, выраженные в долларах, хотя они сейчас выражены в долларах и центах. В целочисленной
арифметике на Форте вы, к примеру, не можете сложить 22.98 и 35.53. Но вы можете произвести
сложение, если выразите доходы в центах, а не в долларах. Другими словами, вы можете изменить
масштаб входных данных й внутреннего представления операций с денежными единицами в 100
раз. В таком случае вы введете
2298 3533 +.
и увидите в результате 5831, что, как вы помните, представляет собой 58 долларов 31 цент. Однако
в таком виде результат выглядит не очень красиво. Вы можете выразить результат в долларах и
центах с помощью следующего слова:
$CENTS 100 /M0D U. " Долларов и ”..’’ центов" ;
Слово $CENTS производит деление числа на 100, округляет результат в сторону нижней грани¬
цы и печатает его как число долларов. Остаток от деления выражает центы. (В гл.5 мы узнаем ме¬
тод, который называется выводом по шаблону, с помощью которого десятичная запятая может быть
помещена в числе в нужном месте). Очень удобно для использования при масштабировании чисел
слово /MOD (его иногда называют масштабным оператором), фактически оно позволяет вам ’’пе¬
ресчитать” число. Число на входе выглядит не очень изящно, поскольку в него нельзя еще ввести
десятичную запятую. Как мы вскоре увидим, при вводе числа двойной длины необходимо указы¬
вать положение десятичной точки, на основании этого Форт распознает числа двойной длины. Но
при расчетах Форт игнорирует положение десятичной точки. Как вы видели на примере $CENTS,
программист не всегда должен заботиться о масштабировании, эта работа может быть оставлена
компьютеру. И не обязательно, чтобы масштабный коэффициент был кратен 10. Вот, например,
слово, которое берет из стека число дюймов и переводит их в футы и дюймы:
FTLN (дюймы —) 12 /M0D.." Футов и”.." дюймов" ;
Очевидно, можно сделать преобразование в обратную сторону. Определим слово
T0INCHES (футы дюймы — дюймы) SWAP 12 * + ;
Если ввести
10 6 T0INCHES,
то мы увидим в стеке длину в дюймах 126.
Умножение на дроби
Предположим, что вы хотите умножить число на 3/4 с помощью калькулятора. Вы можете в дей¬
ствительности умножить на 0.75, зная, что 0.75 равно 3/4. Так как вы не сможете умножить на
0.75 с помощью целых чисел, вы можете на Форте вычислить дробное выражение, умножая на 3/4.
Однако позвольте, просто так нельзя разделить 3 на 4, а потом умножить на результат, так как 3 4
/ дает 0. Поэтому нужно сначала умножить число на 3, а затем произвести деление, т.е. вам нужно
применить оператор */ (он, как и /MOD, называется масштабным оператором). Таким образом,
выражение
3 4 / 100 *
дает неверный результат 0, в то время как
100 3 4 */
дает правильный результат 75. Напомним, что во избежание переполнения */ сохраняет промежу¬
точный результат умножения в виде 32-разрядного числа.
Отсюда вытекает общее правило: чтобы обеспечить надлежащую точность смешанной опера¬
ции, всегда выполняйте операции, которые дают в результате большое число, прежде чем де¬
лать деление, поскольку промежуточный результат получается без переполнения. Это еще одна
причина необходимости разложения выражения на множители перед его вычислением. Например,
51
выражение а/х + b/x + с/х может дать менее точный результат, чем выражение (a+b+c)/x. Чтобы
убедиться в справедливости этого утверждения, проверьте это с какими-либо числами.
Оператор */ очень удобен для умножения на дробь с постоянным знаменателем. Например, вы
можете определить слово PERCENT (процент) :
: PERCENT 100 */ ;
так, что выражение 130 50 PERCENT положит в стек число 65 (50% от 130). В некоторых случаях
вы хотели бы иметь результат смешанной операции */ с большей точностью, чем получается с по¬
мощью обычного умножения с последующим делением.Наилучший метод избежать потери точности
— это убедиться, что перед операцией */ входные операнды уже увеличены с помощью масштаби¬
рования для получения желаемой точности. Например, если вы вычисляете 3/4 от 123, то в плава¬
ющей арифметике вы получите 92.25, а в целочисленной — 92. Следует изменить масштаб чисел,
чтобы операция и */ производилась не с числом 123, а с числом 12300. Это еще один пример того,
что, применяя целочисленную арифметику, программист должен заранее все продумать. Но есть
еще один способ не потерять точность, если вы по какой-либо причине не хотите предварительным
масштабированием увеличить числа: применить оператор */MOD (подобно */, он называется масш¬
табным оператором). Например, выражение
123 3 4 */M0D
выдает на вершину стека частное 92 и вторым сверху — остаток 1. Вы можете использовать оста¬
ток, если не хотите потерять точность, применяя выражение
123 3 4 */M0D SWAP 100 4 */
которое положит в стек 92 25. Это значит, что на вершине стека находится число, которое пред¬
ставляет собой два разряда после десятичной запятой в представлении с плавающей запятой. Если
вы продумаете тщательно программу, то сможете с помощью целых чисел выразить эквивалент
числа с плавающей запятой, а, пользуясь форматным выводом, как описано в гл.5, вы сможете да¬
же напечатать результат с десятичной запятой (например, 92.25).
Приближенное представление чисел с помощью рациональных дробей
Предположим, что вы хотите найти длину периметра круга, которая равна диаметру, умножен¬
ному на число Пи. Как можно выразить иррациональное число Пи, т.е. число, которое не может
быть выражено в виде отношения двух целых чисел? Для приближенного вычисления можно ис¬
пользовать
: PI* 31416 10000 */ ;
Если диаметр равен 10 см, то выражение 10 PI* дает 31 — величину периметра с погрешностью
1 см. Очевидно, вам может потребоваться большая точность, для чего вы можете изменить масштаб
диаметра на входе, например задать его в миллиметрах. Выражение 100 PI* даст величину 314,T.e.
длину периметра 314 мм. С помощью следующего слова вы можете найти площадь круга (которая
равна квадрату радиуса, помноженному на Пи или четвертой части квадрата диаметра, умножен¬
ной на Пи) :
: AREA DUP 4 */ PI* ;
Что делает PI* ? Оно просто умножает число на отношение 31416/10000, которое равно 3.1415.
31416/10000 представляет собой аппроксимацию иррационального числа Пи рациональной дробью,
правда, не очень хорошей аппроксимацией, потому что для невысокой точности всего нескольких
знаков используются большие числа. Дробно-рациональная аппроксимация применяется для боль¬
шого разнообразия иррациональных чисел и физических констант, включая гораздо лучшую апп¬
роксимацию числа Пи. В табл. 4.2 приведено несколько примеров. Заметим, что все эти аппрокси¬
мации дают большую точность, чем можно получить, применяя числа одинарной длины.
Таблица 4.2. Дробно-рациональная аппроксимация некоторых общеупотребительных констант
Константа
Отношение
Ошибка
Пи
355/113
8.5E-8
2
19601/13860
1.5E-9
3
18817/10864
1.1E-9
e
28667/10564
5.5E-9
сСскорость света)
24559/8192
1.6E-9
52
Округление
Если вы делите 26 на 5, то, применяя числа с плавающей запятой, вы получите 5.2, а при деле¬
нии 29 на 5 — 5.8. Однако при целочисленном делении в обоих случаях мы получим 5. Другими
словами, целочисленное деление производит округление с уменьшением (усечение). Если вы будете
складывать результаты деления нескольких чисел, то ошибка округления будет накапливаться и ре¬
зультат будет занижен. Можно при округлении следовать школьному правилу: если на первом мес¬
те после запятой находится цифра 5 или больше, то округление производится в большую сторону,
если меньше 5— то в меньшую. Программа для этого выглядит не очень сложно. Мы приводим про¬
грамму деления с округлением, которая делает все, что нужно:
R/ SWAP OVER /MOD SWAP 2 * ROT / + ,
Испытайте ее:
26 5 R/
даст в результате 5, а
29 5 R/
даст в результате 6. Вот что здесь происходит. Делитель был помещен на дно стека, затем скопиро¬
ван на вершину, чтобы осуществить деление с остатком. Остаток был скопирован, а затем поделен
на исходный делитель. Это второе частное должно быть равно 1, если первое частное должно быть
округлено, причем в этом случае оно должно быть сложено с первым частным. Иначе второе част¬
ное должно быть равно 0 и первое частное должно быть оставлено без изменения. Почему эта про¬
грамма работает? В терминологии деления с плавающей запятой число следует округлить, если
дробная часть частного больше или равна 0.5, т.е. дробная часть должна быть умножена на 2, если
она больше или равна 1. В целой форме дробная часть удваивается, конечно, удвоенный остаток
помещается поверх делителя. Но бывают случаи, когда вы должны сделать округление после деле¬
ния; это случается, когда вы хотите, чтобы сумма последовательности частных имела ошибку в сто¬
рону увеличения или когда вы скорее склонны переоценить результат, чем недооценить его. Чтобы
произвести округление, вы просто добавляете 1 к нормальному частному от деления двух целых чи¬
сел, если остаток не равен 0. Вот слово,которое это делает на Форт-83:
RUP/ /MOD SWAP 0= 0= +
Слово 0= будет более подробно рассмотрено чуть позже, вкратце оно возвращает 1, если на вер¬
шине стека 0, или 0 — в противном случае. Зная это, вы должны понимать, как работает RUP/.
Упражнения
1. Сделайте следующие упражнения в уме:
(а)
5E5x5El0
(б)
5E5x5E-10
(в)
5E5x5E0
(г)
5E-1x5E-5
(А)
5E5/5E10
(e)
5E5/5E-5
(ж)
5E2 + 2E3
(з)
5E2 + 5E-3
(и)
5E2 - 2E-3
(к)
5E2 - 5E-3
2. Показательная форма записи может применяться и для целых чисел, но не часто. Мантисса должна быть сохранена
второй в стеке, а показатель степени — на вершине стека. Таким образом, для 5E10 в стеке находятся числа 5 10. Напишите
слово EXP*, которое должно перемножать два числа, представленных в стеке описанным способом. Например, если в стеке
находятся числа 5 10 6 15, то после умножения в стеке будут находиться числа 30 25. Попробуйте это слово на примерах
упражнения l(a-r).
3. Используя идеи упражнения 2, определите слово ЕХР/для деления чисел, представленных*в показательной форме за¬
писи. Это упражнение не такое простое, как вам кажется, поэтому загляните в ответ в приложении Д, даже если вы чувст¬
вуете, что сделали правильно.
4. Определите слово ТОМ (в_метры), которое складывает длину, выраженную в километрах, с длиной, выраженной в
метрах. Слово предполагает, что в стеке содержится величина в километрах и величина в метрах в указанном порядке. Ре¬
зультат, выраженный в миллиметрах, должен оставаться в стеке.
5. Определите слово TOCMMM (в_см_и_мм), которое берет с вершины стека число, представляющее миллиметры, и пре¬
образует его в сантиметры, располагая результат вторым сверху, поверх которого должно располагаться число миллиметров,
т.е. число 12.345 должно быть преобразовано в 1234 и 5.
6. Определите слово ТОКМ(в_км), которое выполняет преобразование обратное тому, что делает слово ТОМ из упражне¬
ния 4, т.е. берет значение в миллиметрах, а возвращает в стек значение в километрах и метрах.
53
7. Определите слово ТОРТ(в_футы) по аналогии с упражнением 4, но для преобразования числа миль и футов в футы. В
одной законодательной миле содержится 5280 футов.
8.0пределите слово TOMILES(e_Mium) для преобразования футов в мили и футы.
9.0пишите слово РГТОСМ(футы_в_м) для преобразования футов в метры, принимая коэффициент преобразования 1 фут
- 0,305 м.
10. Пользуясь словами и идеями упражнений 6-9, напишите программу ТОМЕТИС(в_метрические) для преобразования
миль и футов в километры и сантиметры, используя расположение данных такое же, как в предыдущих упражнениях.
11. Пересчет градусов Цельсия в градусы Фаренгейта производится по формуле
F = 32 + 9C/5.
Определите слово C->F для преобразования градусов Цельсия в градусы Фаренгейта, при этом температура по Фаренгей¬
ту должна выражаться с погрешностью 0.1 градуса. Позаботьтесь о сохранении погрешности.
12. Напишите слово для определения 1/10 периметра окружности, диаметр которой равен точно 1/2 см. Результат дол¬
жен быть получен с погрешностью 1 нанометр
(1 нм - 1/10000 см). Используйте дробно-рациональную аппроксимацию.
13. Определите слово IN->FT для пересчета дюймов в футы с округлением в сторону ближайшего фута, т.е. 13
дюймов нужно округлить до 1 фута, 20 дюймов — до 2 футов.
Числа двойной длины
Мы уже неоднократно упоминали о числах двойной длины. К примеру, при использовании опера¬
ции */ промежуточный результат умножения запоминается как число двойной длины. Числа двой¬
ной длины записываются в два раза большим числом разрядов, чем числа одинарной длины, т.е.
для хранения одного числа в стеке используется 32 бита, или 4 байта памяти. Они используются
так же, как и числа одинарной длины, за исключением того, что для арифметических операций с
ними применяются другие, хотя и похожие слова. Диапазон представления чисел двойной длины со
знаком составляет от -2 147 483 648 до 2 147 483 647, диапазон чисел без знака от 0 до 4 294 967
295. Иногда числа двойной длины называют числами двойной точности или еще 32-разрядные
числа в стандарте Форта называют двойными числами. Нам кажется, что термин ’’числа двойной
точности” должен распространяться только на числа с плавающей запятой. 32-разрядные целые
числа имеют в два раза большую длину, а величина их, конечно, больше не в два раза, что не оп¬
ределяет их точность. Фактически число ’’двойной длины” означает только то, что число в двоич¬
ной форме занимает в два раза больше разрядов, чем число одинарной длины.
Числа двойной длины представляют собой расширение стандарта языка Форт и других версий,
т.е. бни не присущи стандартному Форту после его загрузки в компьютер. Расширения языка дол¬
жны быть загружены самостоятельно. В версии MMSFORTH представление числа двойной длины
производится словом DBL-LEN# (двойная_длина). Для других версий Форта нужно обратиться к
руководству, чтобы узнать, как это делается, или убедиться, что уже предусмотрено. Если при за¬
грузке Форта это не производится, то нужно осуществить загрузку, после этого мы попробуем сде¬
лать несколько примеров. Для того чтобы их понять, вам нужно знать, что слово D. (произносится
как дэ-точка) печатает число двойной длины, оно является эквивалентом слова, (точка). Попробуем
ввести
1 .23 D
и мы увидим на экране число 123. А теперь введите
123 D
и вы увидите тот же самый результат:
123 ok
Теперь введите
1234567890. D.
и будет выведено
1234567890 ok
Но если ввести 1234567890 (без десятичной точки в конце),то произойдет переполнение, потому
что число было чересчур велико. Не слишком ли это смущает вас? Для чего в числе была нужна
точка? И почему число 1234567890. проходит, а число 1234567890 — нет? Ответ простой. Десяти¬
чная точка сообщает Форту, что число нужно рассматривать как число двойной длины, т. e. оно
должно быть записано в 32 разряда, или 4 байта. При этом совершенно безразлично, где находится
десятичная точка, поскольку она в дальнейшем не используется (вы помните, что числа двойной
длины используются в целочисленной арифметике). (Примечание: десятичная точка игнорируется
54
не всегда. В MMSFORTH и других версиях положение десятичной точки запоминается для того,
чтобы произвести масштабирование чисел, как мы вскоре увидим.) И поэтому если десятичная точ¬
ка отсутствует, то она и не обнаруживается при печати числа. Теперь вам стало понятно, что про¬
исходит в наших примерах. Очевидно, что число 1234567890 приводит к ошибке, так как вы не со¬
общили Форту, что это число двойной длины, а для числа одинарной длины оно слишком велико.
Попробуем еще несколько примеров. Убедитесь, что стек пуст и после этого введите
1 .23
Вы увидите
0 123 ok
В этом случае вы вывели два числа одинарной длины, на вершине стека было число 0 и следую¬
щее число 123. Попробуйте ввести
65535. U. U.
тогда вы получите
0 65535 ok
но если ввести
65536. U U
то вы увидите
1 0 ok
Вы понимаете, в чем тут дело? Если нет, то пропечатайте результаты последних двух примеров в
двоичной форме (2 BASE !). Тогда вы получите такой результат:
0 1111111111111111
для числа 65535 и
1 о
для числа 65536. Если вы переходите от 65535 к 65536, происходит превышение максимального
значения числа одинарной длины (целое число находится на вершине стека), при этом младший
бит второго числа в стеке устанавливается в ”1”. To, что происходит с двумя верхними ячейками
стека, аналогично тому, чтр происходит с двумя байтами в стеке, когда число одинарной длины из¬
меняется с 255 на 256. Другими словами, с числами двойной длины Форт обращается так же, как с
числами одинарной длины, но для них используется 32 бита. И при этом можно отображать числа
двойной длины как со знаком, так и без знака.(Если это вас смущает, просмотрите материал о xpa-
нениии чисел в двоичной форме из гл. 3.)
Для чисел двойной длины применяется набор арифметических операций, полностью аналогичный
набору для чисел одинарной длины, поэтому мы даже не станем приводить примеру. Если вы не
совсем понимаете, что делает тот или иной оператор, посмотрите их определения в приложении А.
Вот эти операторы: D+, D-, DMAX, DMIN, DABS и DNEGATE (а в Форт-83 еще и D2/). Имеется
также набор операторов для сравнения чисел двойной длины, но они будут рассмотрены в гл. 7. В
MMSFORTH и других версиях имеется еще ряд дополнительных арифметических операторов, на¬
пример D*, D/, D*/, D*/MOD и D/MOD. Они действуют так же, как их эквиваленты для чисел
одинарной длины, при этом те слова, которые обеспечивают сохранение промежуточных результа¬
тов операций с числами одинарной длины в виде 32-разрядных чисел, в данном случае сохраняют
промежуточный результат в виде 64-разрядных чисел (т.е. чисел четырехкратной длины).
В MMSFORTH есть два полезных сдова #PT и HI#. Слово #PT запоминает положение десяти¬
чной точки (считая справа налево) в последнем введенном числе двойной длины. Так, например,
12.345 #PT.
выдает число 4, в то время как
1 2345 #PT
выдает число 5. Вы понимаете, что это слово может пригодиться для масштабирования чисел. Если
нет, то из упражнений вам станет ясно, как его использовать. Слово #PT полезно также для фор¬
матного представления чисел, с которым мы познакомимся в гл. 5.
Слово #HI производит удивительное действие. В MMSFORTH все числа воспринимаются как
числа двойной длины, но если в числе нет десятичной точки, то в стеке запоминаются только 16
младших битов числа. Независимо от наличия десятичной точки старшие 16 битов числа запомина¬
ются в слове HI#. Таким образом, если ввести
12345678 HI# D
вы увидите число 12345678. Как работает это слово? Для чисел двойной длины старшие 16 бит за¬
поминаются на вершине стека, слово HI# снимает старшие 16 битов и помещает их в стек. Следо¬
55
вательно, если с клавиатуры вводится число двойной длины, а после него слово HI#, то число всег¬
да запоминается в виде числа двойной длины.
Для манипуляций в стеке с числами двойной длины имеется набор слов, аналогичных словам для
работы с числами одинарной длины: 2DROP, 2DUP,' 20VER, 2ROT и 2SWAP. Для доступа к числам
двойной длины в памяти имеются также два слова 2! и 2@. Но эти слова так похожи на соответст¬
вующие слова для работы с числами одинарной длины, что лучше всего их рассмотреть в упражне¬
ниях.
Упражнения
1. Проверьте, что наибольшее 32-разрядное число без знака равно 4294967295. (Подсказка: вспомните о степенях числа
2.)
2. Рассмотрите табл. 2.2 и постройте аналогичную таблицу для манипуляций в стеке с числами двойной длины.
3. Напишите определение слова 2DROP под именем NEW2DROP.
4. Одинаковы ли по своему действию слова 2DUP и DUP DUP? Если да, то почему? и почему, если нет?
5. Определите слово 2DUP под именем NEW2DUP.
6. Определите слова 2SWAP79 и 2SWAP83 через ROLL.
7. Определите слова 2ROT79 и 2ROT83, используя слово ROLL.
8. Определите слова 20VER83 и 20VER79 через слово PICK. Слова, которые мы приводим в упражнениях 3-9, обычно
для достижения быстродействия определены на языке ассемблера.
9. Определите слова 2ROLL83 и 2ROLL79, которые должны действовать аналогично ROLL.
10. Определите слова 2PICK83 и 2PICK79, действующие аналогично слову PICK, для тех, кто пользуется версией
MMSFORTH.
11. Определите слово S->D для преобразования числа одинарной длины в число двойной длины. (Совет: проанализируй¬
те приведенный пример слова HI# из MMSFORTH.)
12. Что будет находиться в HI#, если вы введете число 123?
13. Вот слово, которое возводит число 10 в степень п, если п находится на вершине стека:
10~N 1 SWAP 0 DO 10 * LOOP ,
Используя слова 10"N, #PT и D/, напишите слово, которое должно возвращать только ту часть числа двойной длины,
которая стоит перед десятичной точкой. И еще напишите слово, которое должно возвращать часть числа, находящуюся после
десятичной точки. (Указание: 123.25 - 123 + 25/100.)
Смешанные действия с числами одинарной и двойной длины
В обоих стандартах (Форт-79 и Форт-83) имеются два обязательных слова для смешанных дейст¬
вий, в которых используются числа одинарной и двойной длины. В расширенных версиях Форта их
еще больше.
В Форт-79 есть слова U* и U/MOD (аналогами их в Форт-83 являются UM* и UM/MOD). Вы
уже раньше узнали, как напечатать число без знака, и, вероятно, вы понимаете, как складывать и
вычитать целые числа со знаком и без знака: никакой разницы нет, с какими числами вы имеете
дело. При умножении имеют значение знаки чисел, кроме того, при умножении больших 16-раз-
рядных чисел может возникнуть переполнение.Слово U* (или UM*) производит умножение двух
чисел без знака, возвращая в стек число двойной длины. Попробуйте ввести
1OO0 1000 U* D
и вы увидите
1000000 ok.
Очевидно, что результат представлен 32-разрядным числом. Теперь попробуйте ввести
5 -5 U* D
тогда вы увидите
327655
Число -5 было воспринято как 65531, поэтому полученное произведение является верным для
данного числа.
Второй оператор для смешанных действий U/MOD (или UM/MOD) производит деление числа
двойной длины (находящегося £ стеке вторым) на число одинарной длины, помещая в стек остаток
и частное в виде чисел одинарной длины. Можете проверить это на ваших собственных примерах.
Кстати сказать, слова U* и UM/MOD (соответственно UM* и UM/MOD) являются частью основно¬
го языка, а не расширения его для чисел двойной длины. Расширяющие слова для смешанных опе¬
56
раций хорошо иллюстрируют слова MMSFORTH. В табл. 4.3 показаны их функции. Из этой табли¬
цы ясно видно, что они делают, а также приведены аналоги для чисел одинарной длины.
Таблица 4.3. Операторы для смешанных действий
Слово
Действие
M*
n n — d
M*/
d n n — d (промежуточ. результат - число тройной длины)
fl+
d n ■— d
M-
d n — d
M/
d n — n
M/M0D
d n — n n
DU*
ud ud — uq
DU/M0D
uq ud — ud ud
Обозначения: n — число одинарной длины; d — число двойной длины; q — число учетверенной
длины, т.е. 64-разрядное; u — без знака.
Упражнения
1. Используйте UM* для преобразования числа одинарной длины в число двойной длины. Имеет ли значение знак?
(Можно сделать это еще быстрее, попросту помещая 0 в стек.)
2. Вспомните, как хранятся в стеке числа двойной длины в старших и младших ячейках. Имея это в виду, определите
следующие смешанные операторы, которые имеются в MMSFORTH: M*, M+, M/ и M/MOD.
3. В чем различие между U/MOD и M/MOD? а также между U* и M* ?
4. Число двойной длины 123.45 помещено в стек и представляет доллары и центы. Определите слово ->DOLLARS, кото¬
рое должно возвращать число долларов в виде числа одинарной длины. Определите другое слово ->CENTS, которое будет
возвращать число центов в виде числа одинарной длины. Проделайте это упражнение, пользуясь словами из MMSFORTH и
стандартными операторами.
5. Определите слово FRAC, которое должно умножать число двойной длины на отношение двух чисел одинарной длины,
т.е. выражение 500. 3 5, после которого стоит это слово, должно давать в стеке значение 300 в виде числа двойной дли¬
ны. Теперь определите это слово, пользуясь смешанными операторами MMSFORTH (последнее определение является триви¬
альным).
Расширение операций над числами с плавающей запятой
Вследствие приведенных раньше в этой главе соображений во многих реализациях Форт включе¬
ны расширенные возможности для работы с числами с плавающей запятой. Поскольку они не ре¬
гламентированы стандартом на числа с плавающей запятой, то слова для операций с числами с
плавающей запятой отличаются от версии к версии.
В MMSFORTH имеется хороший набор расширяющих слов, которые мы используем в качестве
примера. Если в вашем распоряжении есть версия, в которой также используются числа с плаваю¬
щей запятой, то она скорее всего похожа на MMSFORTH и вам, вероятно, будет интересно просле¬
дить за этим обзором, привлекая документацию вашей версии.
Мы уже рассмотрели, что представляют собой числа с плавающей запятой и числа в показатель¬
ной форме. После этого вы, возможно, захотите перейти к материалу о реализации операций с пла¬
вающей запятой в MMSFORTH. Мы предполагаем, что вы уже поняли представление чисел с пла¬
вающей запятой, ’’научную” форму представления чисел, как записываются числа в показательной
форме и арифметические действия с ними.
Во многих компьютерах имеется встроенный интерпретатор языка Бейсик, выполненный на осно¬
ве ПЗУ — постоянной памяти (по-английски ROM — Read Only Memory, т.е. только считываемая
память). Этот интерпретатор, написанный в машинных кодах, производит множество операций над
числами с плавающей запятой. Кроме обычных операций умножения, деления, сложениями вычита¬
ния в нем имеются программы вычисления трансцендентных функций (например, тригонометриче¬
ских и логарифмической), а также большое количество операций с целыми числами. Вследствие то¬
го, что MMSFORTH первоначально был разработан для работы с ЭВМ TRS-80, модель 1, совмести¬
57
мой с IBM PC, и, поскольку обе ЭВМ имеют встроенный Бейсик, ’’зашитый” в ПЗУ, обычные опе¬
рации над числами с плавающей запятой производятся путем вызова машинных программ из ПЗУ.
Так как обе машины могут работать с числами с плавающей запятой, представляемыми 32 и 64
разрядами, то MMSFORTH может оперировать с числами одинарной и двойной точности. Обратите
внимание, что эти числа принципиально отличаются от целых чисел Форта одинарной и двойной
длины (в связи с чем лучше говорить о целых числах как о числах двойной длины, а не двойной
точности). Числа с плавающей запятой одинарной точности обеспечивают 5 значащих разрядов и
диапазон 9.9999E-38 — 1E38. Числа с плавающей запятой двойной точности имеют точность 17
значащих разрядов и диапазон 9.9999999999999999E-38 — 1E38. Правила ввода и вывода чисел при
использовании программ с плавающей запятой из ПЗУ точно такие же, как правила Бейсика уров¬
ня 2 для TRS-80 или Бейсика фирмы Microsoft, и приведены они в документации микрокомпьюте¬
ра. MMSFORTH и большинство других версий Форт для IBM PC также поддерживают арифметиче¬
ский сопроцессор серии 8087, который может быть установлен в персональных компьютерах и дру¬
гих совместимых ЭВМ. Сопроцессор типа 8087 вместе с MMSFORTH позволяет работать с числами,
имеющими 16 значащих разрядов мантиссы и порядок от - 4932 до 4932. Кроме того, он обеспечи¬
вает вычисление трансцендентных функций и всех функций обычной арифметики с плавающей за¬
пятой и ряда других. Однако микросхема 8087 выполняет все расчеты не с помощью процедур на
машинном языке, а с помощью аппаратных средств, которые представляют собой часть электрон¬
ных схем процессора 8087. Аппаратная арифметика микросхемы 8087 является очень быстродейст¬
вующей, она в 100 раз быстрее, чем эквивалентная математика на машинном языке и чаще всего
быстрее, чем арифметика целых чисел (см. гл. 8). Мы опишем два варианта реализации в
MMSFORTH арифметических операций с плавающей запятой: в ПЗУ и на базе сопроцессора 8087.
Арифметика с плавающей запятой, реализованная на ПЗУ
Краткие сведения о функции арифметики с плавающей запятой, реализованной в MMSFORTH
на базе ПЗУ, приведены в табл. 4.4. В этом разделе мы опишем некоторые характерные черты этой
арифметики, а в следующем — ее отличия от реализации на сопроцессоре 8087.
Числа с плавающей запятой представляются в стеке с 32 разрядами при единичной точности и с 64
разрядами при двойной точности. Таким образом, все слова, которые применяются с целыми числа¬
ми двойной длины, можно использовать с числами с плавающей запятой одинарной точности,
включая слова для задания констант и массивов, которые будут рассмотрены в гл. 6. Перед вводи¬
мым числом с плавающей запятой должен быть знак %. Ввод производится в свободном формате,
поэтому % 1.01E2, % 10.1 и % .10100E3 будут в результате помещать в стек одно и то же число
10.1 в старших 32 битах как число с плавающей запятой. D% будет помещать в стек число с плава¬
ющей запятой двойной точности. Арифметические и другие математические действия производятся
так же, как с целыми числами, поэтому мы их обсуждать не будем. Например, % .551E3 % 10 F+
TAN F. произведет суммирование чисел 55.1 и 10, получая 65.1, а затем выведет значение тангенса
этой величины (по умолчанию — в радианах), которая равна -1.19366. Что же касается комплекс¬
ных чисел, то они заслуживают дальнейшего рассмотрения, но мы отложим этот вопрос до реализа¬
ции операций с плавающей запятой на базе сопроцессора 8087. Использование слов для работы с
числами с плавающей запятой будет понятно из упражнений.
Упражнения
1. Оцените значения следующих выражений, вводя числа с клавиатуры:
(а) 5.5 + 1200 (б) ln(23/5)
(в) s‘in~2(55) + cos~2(45) (углы - в градусах)
(г) длину стороны квадрата с площадью 10 квадратных дюймов
(д) площадь круга с радиусом 3 25 дюйма.
2. Напишите слово, которое будет брать два целых числа из стека, вычислять площадь прямоугольника со сторонами, вы¬
ражаемыми этими числами, и печатать ее, обеспечивая не менее 10 значащих разрядов.
3. Напишите определение слова FABS с именем NEWFABS, используя возведение в квадрат и квадратный корень.
4. Напишите слЬво для определения гипо*енузы прямоугольного треугольника по теореме Пифагора.
Следующее упражнение предназначено для более подготовленных.
5. Напишите слово, которое с помощью цикла DO-LOOP печатало бы таблицу значений синуса и тангенса для углов от 0
до 45 градусов с шагом 1 градус. To же самое сделайте с шагом 0.1 градуса.
58
Таблица 4.4. Набор операций над числами с плавающей запятой, реализованных в MMSFORTH на базе ПЗУ*
v%
( ~)
Предшествует вводу числа с плавающей запятой
F#IN
( — f)
Запрашивает ввод числа с плавающей запятой с клавиатуры
F.
( f —)
Печатает число с плавающей запятой
F.R
( f ширина_поля —)
Печатает число с плавающей запятой,
выравнен- ное вправо в поле указанной ширины
F+
( f1 f2 — f3)
Возвращает сумму двух чисел
F-
( f1 f2 — f3)
Возвращает разность двух чисел f1-f2
F*
( f1 f2 — f3)
Умножает два числа f1*f2
F/
( f1 f2 — f3)
Делит два числа (f1/f2)
FABS
( f — f1)
Берет абсолютную величину числа с плавающей запятой
FMINUS
( f — -f>
Изменяет знак числа; эквивалентно NEGATE
SGN
( f — n)
Возвращает -1, если f<0, 0, если f=0, 1, если f>0
FCOMP
( f1 f2 — n)
Сравнивает два числа с плавающей запятой;
n=-1, если f1<f2, 0, если f1=f2, 1, если f1>f2
LOG
( f — log(f))
Возвращает натуральный логарифм числа f
L0G10
( f — lg(f))
Возвращает десятичный логарифм числа f
EXP
( f — exp(f))
Возвращает значенив'числа e в степени f
10~
( f — 10f)
Возводит число 10 в степень f
X~Y
( f1 f2 — f1f2)
Возвращает f1, возведенное в степень f2
1/Х
( f — 1/f)
Возвращает число, обратное f
FIX
( f1 — f2)
Возвращает число с плавающей запятой,
округленное до целого
INT
( f1 — f2)
Возвращает вместо числа с плавающей запятой
ближайшее целое снизу
CINT
( f ~ n)
Возвращает 16-разрядное ближайшее целое, меньшее f
I-F
( n — f)
Преобразует 16-разрядное целое число в число
с плавающей запятой
SQR
( f1 — f2)
Возвращает квадратный корень от f1
RND
( f — f)
Возвращает случайное число с плавающей запятой,
как на Microsoft Бейсике
DEGREES
( —)
Дает указания Форту принимать углы в градусах
RADIANS
( —>
Дает указания Форту принимать углы в радианах
SIN
( f1 — f2)
Возвращает значение синуса от f1
COS
( f1 — f2)
Возвращает значение косинуса от f1
TAN
( f1 — f2)
Возвращает значение тангенса от f1
ATN
( f1 — f2)
Возвращает значение арктангенса от f1
ATN2
( f1 f2 — f3)
Возвращает значение арктангенса угла отрезка,
соединяющего начало координат с точкой x,y
PI
( ^ f)
Возвращает константу Пи
RAD
( — f)
Возвращает константу для перевода градусов
в радианы
L10
( — f)
Возвращает натуральный логарифм 10
DF#IN
( — df)
Запрашивает ввод числа с плавающей запятой
двойной точности с клавиатуры
DF
( df —>
Печатает число с плавающей запятой двойной точности
DF.R
( df ширина_поля—)
Печатает число с плавающей запятой
двойной точности, выравненное в поле
указанной длины вправо
DF+
( dfl df2 df3)
Возвращает сумму (df1 + df2)
DF-
( df1 df2 — df3)
Возвращает разность (df1 - df2)
DF*
( dfl df2 — df3)
Возвращает произведение (df1*df2)
DF/
( dfl df2 — df3)
Возвращает частное (df1/df2)
DFABS
( dfi — df2)
Возвращает абсолютное значение df1
DFMINUS
N ( dfl — df2)
Изменяет знак df1
DSGN
( df — n)
Возвращает -1, если df<0; 0, если df = 0; 1,
если df>0
DFCOMP
( dfi df2 — n)
Сравнивает два числа с плавающей запятой
двойной точности, возвращая -1,
если dfKdf2; 0, если dfl=df2; 1, если df1>df2
FDF
( f — df)
Преобразует число с плавающей запятой
одинарной точности в число двойной точности
с плавающей запятой
DFF
( df — f)
Преобразует число с плавающей запятой
двойной точности в число одинарной точности
с плавающей запятой
CP+
( cp1 cp2 — срЗ)
Возвращает сумму двух комплексных чисел
CP-
( cp1 cp2 — срЗ)
Возвращает разность двух комплексных чисел (cp1 cp2)
CP*
( cp1 cp2 — срЗ)
Возвращает произведение двух
комплексных чисел (cp1*cp2)
CP/
( cp1 cp2 — срЗ)
Возвращает частное от деления двух
MAG
комплексных чисел (cp1/cp2)
( cp — f)
Возвращает модуль комплексного числа
PHASE
( cp — f)
Возвращает аргумент или фазу
комплексного числа
CPMINUS
( cp cp)
Изменяет знак комплексного числа
с плавающей запятой
59
CONJG ( cp — cp) Возвращает сопряженное комплексное
число с плавающей запятой
R P ( cp — f f) Преобразует прямоугольные координаты в полярные
P R ( f f — cp) Преобразует полярные координаты в
прямоугольные
Набор программ содержит также несколько слов для работы с числами_двойной и учетверенной длины,
например 4SWAP. Обозначения: f - число с плавающей запятой; df - число двойной точности с плаваю¬
щей запятой; cp - комплексное число с плавающей запятой; n - 16-битовое число со знаком; адр - ад¬
рес .
Арифметика с плавающей запятой, реализованная на сопроцессоре 8087
Реализация операций с плавающей запятой на основе микросхемы 8087 отличается от реализа¬
ции на ПЗУ несколькими существенными моментами:
1) вместо стека в памяти ЭВМ используется стек микросхемы, поэтому версии Форта могут
иметь различия; преимущество этого стека — в быстродействии, недостаток — в ограниченной глу¬
бине стека;
2) большая часть операций выполняется на аппаратном уровне микросхемы 8087, а не на уровне
машинных программ;
3) числа в стеке микросхемы 8087 хранятся как 80-битовые, а в памяти как 64-битовые, что дает
дополнительные 16 знаков точности по сравнению с 64-битовыми числами, и 9 разрядов для 32-би-
товых чисел. Диапазон значений порядка от -4932 до 4932 (80 битов), от -306 до 307 (64 бита) и от
-38 до 38 (32 бита);
4) скорость операций с плавающей запятой на микропроцессоре 8087 в 100 раз выше, чем на
ПЗУ, и для некоторых операций даже превосходит скорость вычислений с 16-разрядными целыми
числами; умножение на микросхеме 8087 производится на 45% быстрее, чем с целыми числами, а
сложение на 5% медленнее;
5) гарантируется обычно 16 точных разрядов при фиксированном формате мантиссы.
Ввод чисел так же, как и в варианте на ПЗУ, производится в свободном формате, за исключени¬
ем того, что диапазон вводимых чисел увеличивается. А так как в микросхеме имеется отдельный
стек, то можно использовать оба стека одновременно без взаимных помех. Например, можно напи¬
сать выражение
% 1E50 55 % 1E10 236 + F* F
тогда получится следующий результат:
291 0.1000000000000000E60
Это значит, что вычисления с целыми числами и числами с плавающей запятой производятся од¬
новременно и независимо в двух стеках: складываются числа 55 и 236 и перемножаются 1E50 и
1E10 и печатаются оба результата. Конечно, такая запись может привести к путанице и поэтому не
рекомендуется. Скорость работы микросхемы 8087 в режиме плавающей запятой удивительна: она
превосходит скорость микрокомпьютеров и приближается к скорости универсальных вычислитель¬
ных машин. Время вычисления 100.000 операций сложения с плавающей запятой составляет чуть
больше 10 с, причем большая часть времени затрачивается на организацию зацикливания програм¬
мы. Очень мощная универсальная ЭВМ Cyber фирмы CDC на языке Фортран затрачивает на это
немного более 1 с, но при этом гарантируется только 12 разрядов точности, в то время как на со¬
процессоре 8087 — 16 знаков точности. Становится реальным вычисление таких выражений, как
1000!, что практически невозможно даже на мини-ЭВМ. Практический пример применения опера¬
ций в плавающей арифметике из работы авторов: нужно было смоделировать процесс изменения
свойств воды в озере под действием естественных процессов за период 1000 лет. Для моделирования
необходимо было решать численными методами 15 дифференциальных уравнений на каждые 4 ч
моделируемого процесса. На суперЭВМ CDC Cyber в режиме разделения времени решение заняло
один день. При этом ЦПУ машины было занято приблизительно около часа, причем стоимость ма¬
шинного времени составила несколько сотен долларов. Решение этой задачи на языке Бейсик в
скомпилированной форме заняло бы более одного месяца работы микроЭВМ IBM PC в монопольном
режиме. При использовании MMSFORTH на микросхеме 8087 результат был получен менее чем за
один день, фактически быстрее, чем на суперЭВМ. Решение немногих задач, подобных описанной,
окупают затраты на микрокомпьютер и программное обеспечение и очень экономно расходуют вре¬
мя большой ЭВМ.
Отсутствующие функции плавающей арифметики, реализованные на ПЗУ: F.R, FABS, FMINUS
(вместо нее имеется FNEGATE), FIX, INT, CINT, I-F, RND, RAD и L10. Кроме того, исключены
60
слова для манипуляций в стеке с числами учетверенной и двойной длины, так как в стекс микро¬
процессора 8087 они не требуются.
Вследствие того, что арифметические действия с плавающей запятой реализованы в микросхеме
8087 аппаратными средствами и отличаются от реализации на ПЗУ, набор программ MMSFORTH
содержит некоторые новые функции, а другие были исключены. Однако все отсутствующие функ¬
ции легко можно определить на основе имеющихся, и мы в этом убедимся в следующих упражне¬
ниях.
Таблица 4.5. Дополнительный набор функций с плавающей запятой, реализованных на микросхеме 8087, не обеспечи¬
ваемых в реализации на ПЗУ
$F.
(8f — адр n)
Преобразует число с плавающей запятой в адрес,
где записаны символьная строка и ее длина n;
удобно для оператора TYPE
87>
(8f — n)
Преобразует число с плавающей запятой в 16-битовое число
>87
(n — 8f)
Преобразует 16-битовое целое в число с плавающей запятой
9DEGREES
(— n)
Возвращает 1, если значение угла вводится
в градусах, 0 - если в радианах
COS.SIN
(8f — 8f 8f)
Возвращает на вершину стека
синус, вторым сверху - косинус
CP*R
(Cp 8f — cp)
Умножает каждую часть комплексного числа
на число с плавающей запятой
CPNEGATE
(cp1 cp1)
см. CPMINUS в табл. 4.4
D87>
(8f -- d)
Преобразует число с плавающей запятой
в 32-битовое целое число
D>87
(d — 8f)
Преобразует 32-битовое целое в число с плавающей запятой
DF87>
(8f — df)
Преобразует число с плавающей запятой
из формата микросхемы 8087 в число двойной точности
с плавающей запятой формата ПЗУ
DF>67
(d ~ 8f)
Преобразует число двойной точности с плавающей запятой
из формата ПЗУ в число с плавающей запятой
формата микросхемы 8087
F87>
(8f -- f)
Преобразует число с плавающей запятой
из формата микросхемы 8087 в формат ПЗУ
F>87
(f — 8f)
Преобразует число с плавающей запятой
из формата ПЗУ в формат микросхемы 8087
FDROP
(8f —)
Очищает стек микросхемы 8087
FDUP
(8f1 — 8f1 8f1)
Эквивалент DUP для микросхемы 8087
FNEGATE
(8f1 — 8f2)
См. FMINUS в табл. 4.4
F0VER
(8f1 8f2 — 8f1
8f2
8f1)
Эквивалент 0VER для 8087
FR0T
(8f1 8f2 8f3 —
8f2
8f3 8f1)
Эквивалент R0T в стеке 8087
FSWAP
(8f1 8f2 — 8f1
8f1)
Эквивалент SWAP в стеке 8087
FVAL
(8f1 -- $)
Возвращает адрес символьной счетной строки,
если задано число в формате 8087
L0G2
(8f1 — 8f2)
Возвращает логарифм числа с плавающей запятой по основанию
ONE
<— 8f)
Помещает в стек 8087 число 1.0000000...
Примечания: обозначения такие же, как в табл. 4.4; 8f - число с плавающей запятой в формате стека 8087; $ - счешая
строка (см. гл. 9).
61
О мнимых и комплексных числах
MMSFORTH — один из немногих языков программирования для микрокомпьютеров, который
может работать с комплексными числами. Если вам нужны для работы комплексные числа, то вы,
конечно, представляете, что это такое. Тем не менее мы приводим краткий обзор для интересую¬
щихся и тех, кто хотел бы узнать об операциях с комплексными числами, даже не в MMSFORTH.
Комплексное число представляет собой сумму действительного и мнимого числа, т.е.
а + bi,
где а называется действительной частью, b — мнимой частью, а i является корнем квадратным из -
ЫДействительное число, как отсюда следует, это ’’нормальное”, не мнимое число).Так как не су¬
ществует такого числа, квадрат которого является каким-либо отрицательным числом, то i не имеет
физического смысла и называется мнимым числом или просто мнимым. Тем не менее мнимые чис¬
ла порождаются в результате некоторых математических действий. И мнимые, и комплексные чис¬
ла широко используются учеными и инженерами. Комплексные числа проще всего представить как
векторы или, еще проще, как точки с координатами (x,y) в системе координат, называемой комп¬
лексной плоскостью, плоскостью Гаусса или круговой диаграммой Аржана. Величина а отображает
координату x, b — координату у. Модуль, или величина, числа, — это длина отрезка, проведенного
из начала координат в данную точку, в то время как угол между осью x и отрезком называется ар¬
гументом или фазой. Сопряженное комплексное число — это число, у которого мнимая часть ум¬
ножается на -1, т.е. сопряженным к a+bi является число a-bi. Слова MMSFORTH MAG (от
magnitude— модуль, величина), PHASE (фаза) и CONJG (сопряженный) делают именно то, что от
них можно ожидать по названию (см. табл. 4.4). В MMSFORTH действительная и мнимая части
числа помещаются в стек так, что на вершине находится мнимая часть. В реализации на ПЗУ они
вводятся с помощью слова CP%, хотя их можно так же просто вводить как два числа с плавающей
запятой, как это должно делаться в реализации на сопроцессоре 8087 в MMSFORTH. В таблице 4.4
показано, каким образом MMSFORTH позволяет выполнять умножение комплексного числа на дей¬
ствительное число, умножение, деление, сложение и вычитание двух комплексных чисел, определе¬
ние модуля и фазы, а также нахождение сопряженного комплексного числа. MMSFORTH позволяет
также преобразовывать представление точки в системе прямоугольных координат в полярные коор¬
динаты. Последние можно рассматривать как форму обращения с комплексными числами, которая
заменяет число с компонентами а и b, выражая его через модуль и аргумент (фазу). (В прямо¬
угольной системе координат точка представляется величинами x,y; в полярной системе координат
— величиной отрезка, проведенного из начала координат в данную точку, и углом между этим от¬
резком и осью x, т.е. фазой.)
Следует заметить, что выражение ’’действительные числа” употребляется специалистами по ком¬
пьютерам некорректно, так как у математиков оно имеет другой смысл. Многие специалисты по
компьютерам считают, что действительные числа — это синоним чисел с плавающей запятой, в от¬
личие от целых чисел. Но это совершенно неправильно: любое число, которое не является мнимым
или комплексным, является действительным, будь оно целое или с плавающей запятой. Неверная
терминология, возможно, обязана своим происхождением условностям, принятым в Фортране, где
числа с плавающей запятой называются ’’действительными” (real). К сожалению, эта терминология
проникла и в описание сопроцессора 8087 и ее следует избегать.
Если у вас есть набор программ MMSFORTH для работы с комплексными числами или какая-ли-
бо другая версия, вы сможете проделать следующие упражнения.
Упражнения
1. Следующие выражения могут быть вычислены в реализации функций с плавающей запятой на микросхеме 8087. Пе¬
репишите их, чтобы можно было работать в версии MMSFORTH, реализованной на ПЗУ:
(а) $ 5.5 5 % 6.5 8 + F+
(б) $ 35 10 I-F F+ SIN
(в) % -55 FMINUS
(г) $ 35 FDUP 5 COS FSWAP SIN F+ . F.
2. Определите слова Форта для .микросхемы 8087, сходные по звучанию и функциям со следующими словами, реализо¬
ванными на ПЗУ: FABS, CINT, I-F, RAD, LI0.
62
3. Определите слово FACT, предназначенное для вычисления факториала в реализации на микросхеме 8087. Попробуйте
вычислить 1000! Зафиксируйте время исполнения операции. (Факториал числа 3 равен 3x2x1, факториал 6 —
6x5x4x3x2xl.)
4. Определите величину ошибки дробно-рациональной аппроксимации числа Пи, приведенной в табл. 4.2.
5. Используя комплексные числа, определите слово ANGLE (угол), для того чтобы найти угол между гипотенузой и при¬
легающей стороной прямоугольного треугольника, если в стеке находятся значения длин обеих прилегающих сторон треу¬
гольника (рис.4.1).
Рис. 4.1
6. Аналогично определите слово HYPOT (гипотенуза) для вычисления длины гипотенузы.
7. По определению, величина pH представляет собой взятый со знаком минус логарифм концентрации ионов водорода:
pH--log[H -].
Определите слово, которое по значению в стеке величины pH и абсолютному значению увеличения концентрации ионов
водорода, выраженному в форме с плавающей запятой, рассчитывает фактическое увеличение pH. Можете ли вы предло¬
жить способ, как сделать это в целочисленнои арифметике?
8. Одна из рутинных задач робототехники — преобразовать перемещение плеча, описываемого координатами x,y, в при¬
ращения радиуса и угла. Например, на рис.4.1 для перемещения из точки (xl,yl) в точку (x2,y2) требуется приращение уг¬
ла (угол1-угол2) и приращение радиуса (радиус1-радиус2). Определите слово ARMMOVE (движение плеча), которое по за¬
данным в стеке значециям xl,yl,x2,y2 будет выдавать в стек значения величин (угол1-угол2) и (радиус1-радиус2). За поло¬
жительное приращение угла принимается движение по часовой стрелке. Для решения вам потребуются тригонометрические
функции или комплексные числа.
ВЫВОДЫ
Как и обещали в начале этой главы, мы привели обзор основных понятий, связанных с числами. Некоторые из них, как,
например, числа двойной длины, должен изучить каждый программирующий на Форте, в то же время числа с плавающей
запятой могут потребоваться только тем, кто будет иметь дело с математическими функциями. Тем не менее после этого об¬
зора вы должны почувствовать, что Форт имеет мощные средства для решения математических научно-технических задач.
Не иронично ли, что многие критики Форта считают его слабостью то, что он не подходит для таких задач? Основанием для
критики является в основном использование обратной польской записи вместо алгебраической и отсутствие операций с дей¬
ствительными числами (в том числе с плавающей запятой) в стандартах языка и его простейших реализациях. Но ни одна
из этих причин не становится трудно преодолимым препятствием для Форта. Применение стека обеспечивает Форту высокое
быстродействие, которое так необходимо для математических приложений, а операции с плавающей запятой имеются в
большинстве версий Форта. Форт используется в очень широком диапазоне отраслей науки и техники, например в лазерной
энергетике, радиоастрономии, технике охраны окружающей среды, физической океанографии, робототехнике. Многие поня¬
ли, что Форт более мощный и простой язык, чем "классический” язык науки и техники Фортран. Мы надеемся, что язык
найдет еще большее распространение среди тех, кто использует математику в технических дисциплинах.
63
Глава 5
ВВОД и вывод символов
Каким образом информация выводится из компьютера на дисплей и принтер и как ее ввести с клавиатуры? Конечно, вы
уже вводили и выводили данные, нажимая клавиши и пользуясь словами . (точка), D., EMIT и т.д. Но имеются и другие
возможности. Поскольку Форт не предназначен для конкретного типа микрокомпьютера, его слова для операций ввода и вы¬
вода — универсальные (большинство версий Бейсика, напротив, приспособлены для определенных типов ЭВМ). Правда, во
многих версиях Форта также имекУгся специальные слова для управления вводом-выводом, чтобы максимально использовать
возможности конкретного микрокомпьютера. Мы рассмотрим как стандартные слова, так и, в качестве примера, некоторые
слова для осуществления ввода-вывода из MMSFORTH. Некоторые вопросы мы отложим до гл. 9, потому что все подробно¬
сти ввода чисел и символьных строк при исполнении программ будет проще понять после того, как вы изучите ввод символь¬
ных строк. Здесь полезно вспомнить, что буквы и числа вводятся в ЭВМ и выводятся одинаково в виде кодов ASCII. Это
объясняется историческими причинами, знание которых поможет вам лучше понять методы ввода и вывода в Форте.
До появления персональных компьютеров ввод и вывод на ЭВМ производился через терминал. В своем простейшем виде
терминал был малоинтеллектуальным устройством. Хороший терминал представляет в сущности пишущую машинку, и фак¬
тически в 1960—1970 гг. наиболее распространенной была модель электрической пишущей машинки типа IBM Selectric.
Другая распространенная модель типа Teletype тоже представляла собой пишущую машинку, но с необычной механикой.
Рассмотрим, что может делать пишущая машинка. Она может выводить на бумагу буквы, цифры и пробелы и, кроме того,
передвигать с помощью валика бумагу. Перемещение бумаги может быть построчное и, кроме того, постраничное. При неко¬
тором умении можно продвинуть бумагу на половину строки или даже перемотать ее в обратную сторону. Конечно, возмож¬
но также переместить каретку на одну позицию по строке назад и перебить символ. Но это почти все, что может делать тер¬
минал как пишущая машинка. Клавиатура терминала также подобна клавиатуре пишущей машинки, т.е. у нее есть клави¬
ши для букв и цифр, пробела, перевода строки и возврата на позицию влево. И только двух дополнительных клавиш, кото¬
рые есть у терминала, нет на пишущей машинке. Это клавиша управления и клавиша спецсимвола Escape (отказ). Для про¬
стого механического терминала при небольшом количестве функций было вполне достаточно кодов управления ASCII. Фак¬
тически терминалы ЭВМ и коды ASCII были прямо заимствованы из телетайпной техники 1930 — 1950 гт. Но с изобретени¬
ем видеодисплейных терминалов открьшись новые возможности.
Первые видеотерминалы работали так же, как и их механические прототипы. Они печатали символы в нижней части эк¬
рана и продвигали строки на экране вверх, подобно листу бумаги в пишущей машинке. Но возможности видеотерминалов
были гораздо богаче. Например, они обладали возможностью разделять экран на две половины по горизонтали или по верти¬
кали с различной информацией в каждой части или, например, выделять слова, меняя яркость. На более сложных термина¬
лах, например Tektronix graphics, можно с высокой разрешающей способностью отображать рисунки линиями. Однако по-
прежнему они общаются с управляющей ЭВМ, фактически используя старый код ASCII. (В действительности есть два важ¬
ных исключения — еще один код для телекоммуникаций, код Бодо, названный по фамилии его изобретателя, пионера в об¬
ласти автомагичесюй телеграфии; кроме того, до сих пор при работе с ЭВМ фирмы IBM используется предложенный ею код
EBCDIC.)
Более высокий уровень сложности стал возможен с изобретением персонального компьютера, содержащего в себе все обо¬
рудование ЭВМ. Его экран может рассматриваться как экран телевизора, отображающего сложные графические образы в
цвете, с набором различных шрифтов, символов, с окнами, в которых отображаются выходные данные различных программ,
включая графические, и многое другое. Достаточно одного взгляда на экран персональной ЭВМ Apple Lisa или Mackintosh,
чтобы убедиться в их огромных возможностях.
Конечно же, и возможности клавиатуры также усложнились с введением специальных функциональных клавиш, клави¬
ши для перемещения курсора и т.д. Однако проблема состоит в том, что по-прежнему ввод и вывод производятся по стандар¬
ту ASCII, хотя в большинстве случаев добавляется еще 127 символов и, таким образом, используется полный набор из 256
символов, каждый из которых может быть представлен одним из 8-разрядных чисел, так называемым байтом. Другая про¬
блема состоит в том, что почти в любой модели терминала и компьютера используются различные способы управления фун¬
кциями экрана. Определенные управляющие коды, например стирание влево, перевод строки, возврат каретки, т.е. те коды,
которые применялись для управления телетайпами, оставили стандартными. Но другие были произвольно изменены. Можно
ли в свете сказанного создать язык, способный работать на разнообразных ЭВМ? Сделать это чрезвычайно трудно. Поэтому
стандарт языка Форт должен офаничивать только ввод-вывод кодов ASCII в расчете на использование простых терминалов.
Для конкретных ЭВМ должны быть разработаны расширенные и нестандартные версии языка. Кроме вывода символов кода¬
ми ASCII терминал должен управляться также либо кодами ASCII, либо так называемыми Esc-последовательностями. Мы
рассматривали управляющие коды в гл. 3, здесь уместно вкратце упомянуть об Esc-последовательностях.
Код ASCII 27 называется Esc-префиксом или Esc-клавишей (отказ). Его можно ввести с клавиатуры ЭВМ, нажимая кла¬
вишу <Esc>. (Некоторые ЭВМ не имеют специальной Esc-клавиши, в этом случае она имитируется некоторой комбинацией
64
других клавиш; например, ввод кода Esc, если нет специальной клавиши, производится одновременным нажатием управляю¬
щей клавиши Ctrl, клавиши Shift (переключение регистров) и <K>.) Назначение клавиши Esc состоит в том, чтобы сигнали¬
зировать, что некоторая последовательность символов после Esc должна рассматриваться как команда управления, а не код
символа ASCII. Например, последовательность Esc С (27,67) должна произвести очистку экрана терминала и помещение
курсора в левый верхний угол. Печатающее устройство также, как правило, управляется Esc-последовательностями, напри¬
мер последовательность кодов (27,28) дает принтеру Centronics 739 команду продвинуть бумагу на полстроки вперед; (27,30)
— на полстроки назад, а последовательность кодов (27,47,48) переводит устройство в режим, при котором следующие байты
воспринимаются не как символы, а как графические команды. Ради чего мы приводим здесь это описание терминалов?
Только ради того, чтобы принести извинения за отсутствие подробных объяснений, как управлять терминалом вашего компь¬
ютера или принтером. Вам нужно практически проверить, как реагирует на Esc-последовательности оборудование вашей
ЭВМ. Мы расскажем здесь, как организовать посылку символов из Форта, а вы сами посмотрите, что они делают на вашем
дисплее или принтере.
Вывод символов
В конце гл. 3 вы уже видели, что самое необходимое слово для вывода символов — EMIT, кото¬
рое посылает на экран дисплея символ, соответствующий числу, которое хранится в стеке. Попро¬
буйте посмотреть, что делают различные управляющие коды, экспериментируя со словом EMIT.
Например, если вы введете
48 EMIT 49 EMIT 10 EMIT 50 EMIT
то скорее всего увидите 01 на одной строке и 2 — на следующей. Почему? Потому, что 48, 49 и 50
представляют собой соответственно ASCII-коды цифр 0, 1 и 2, а 10 — перевод строки, который при
интерпретации обычно сопровождается возвратом каретки.
Если вы напечатаете
48 EMIT 49 EMIT 8 EMIT 50 EMIT
то увидите 02. Цифра 1 была стерта, так как код 8 представляет команду перемещения курсора на
позицию влево, или стирания влево (Backspace). Испробуйте другие управляющие коды, чтобы ус¬
тановить их действие на вашем оборудовании, при этом могут возникнуть некоторые сюрпризы, на¬
пример в MMSFORTH на машине IBM PC или на TRS-80 команда 27 EMIT приводит к обратной
подаче строки, в то время как 12 EMIT очищает экран и возвращает курсор ’’домой” (т.е. в левый
верхний угол экрана). Вы можете также попробовать коды ASCII от 128 и более, которые не явля¬
ются стандартными и могут выводить странные символы и необычные буквы или продемонстриру¬
ют, что будет на экране, если из кода ASCII вычесть 127, т.е. если игнорировать значение старшего
бита.
Можно определить через EMIT два стандартных слова, действие которых столь очевидно, что
мы приводим только их определение:
SPACE 32 EMIT ; (пробел)
И
CR 13 EMIT 10 EMIT ; (перевод строки + возврат
каретки)
Не столь очевидно определение стандартного слова ’’пробелы”:
SPACES 0 D0 SPACE L00P ;
Оно выводит пробелы, количество которых определяется числом на вершине стека. Нестандарт¬
ное слово BL (пробел) включено во многие версии Форта. Оно помещает на вершину стека 32, т.е.
ASCII-код пробела. Таким образом, можно дать иное определение пробела:
: SPACE BL EMIT ;
Более сложное слово с использованием EMIT — это TYPE (печать). Его подробное описание
вы найдете в гл. 9, где мы объясним символьные строки, но, чтобы начать пользоваться им раньше,
мы рассмотрим его уже сейчас. Слово TYPE просматривает последовательность однобайтовых (8-
разрядных) чисел (символов) из памяти и посылают их ASCII-эквивалент на экран. Для него тре¬
буется число на вершине стека, а ниже него — адрес. Слово TYPE выводит указанное число сим¬
волов, начинающихся с этого адреса, на экран. Рассмотрим пример. Может быть, вам потребуется
освежить в памяти счетные циклы DO-LOOP (гл. 1) и слово PAD из гл. 4. Слово I помещает
текущее значение параметра цикла на вершину стека. Определим слово
: PUTTEST 84 83 69 84 4 0 D0 PAD I + С! L00P ;
которое поместит символы ASCII символьной строки ’’TEST” в памяти, начиная с адреса, выдавае¬
мого словом PAD. Теперь введите с клавиатуры
65
з М. Келли
PAD 4 TYPE
и на экране появится строка ”TEST”. Обратите внимание, что коды ASCII должны быть помещены
в стек в обратном порядке. А вот одно из возможных определений слова TYPE; попробуйте разо¬
браться, как оно работает :
TYPE (адр n —) 0 DO DUP I + C@ EMIT LOOP DROP ;
Теперь нам следует познакомиться с символьными строками, о которых более подробно вы узнае¬
те из гл. 9. Чтобы напечатать или сделать что-нибудь еще с символьной строкой, нам надо знать не
только входящие в нее символы, но также их количество.
Один из способов запоминания строки, стандартный для Форта, состоит в использовании счетной
строки (иногда ее называют нумерованной строкой). Счетная строка представляет собой область па¬
мяти, первый байт которой содержит число символов строки, за которым следуют собственно коды
ASCII. Мы можем переопределить слово PUTTEST, помещающее строку ”TEST” в PAD, следу¬
ющим образом:
. PUTTEST 84 83 69 84 4 PAD С! 4 0 DO PAD I 1+ + С! LOOP ;
4 PAD С! помещает число символов (счетное число) в PAD. Остальная часть определяемого
слова размещает строку символов в более старшие адреса. Чтобы посмотреть строку, нужно ввести с
клавиатуры
PAD 1+ PAD C@ TYPE
После этого в стеке будет находиться адрес начала строки и на вершине стека — число символов,
т.е. стек подготовлен для распечатки словом TYPE. Поскольку этот принцип широко используется,
предусмотрены стандартные слова для получения числа символов и адреса символьной строки:
COUNT (адр — адр+1 n) DUP 1+ SWAP C@ ,
Поэтому вы сможете увидеть тестовое слово в PAD с помощью
PAD COUNT TYPE
В гл. 9 вы увидите, что счетные строки обеспечивают большую гибкость при манипулировании с
символьным текстом. Для примера скажем, что эта книга была набрана с помощью редактора тек¬
ста, написанного на Форте, и эта программа редактора работает не хуже любой подобной програм¬
мы, написанной на языке ассемблера.
В гл. 1 вы познакомились с более простым способом вывода строк с помощью слова .” (точ¬
ка—кавычка), т.е.
" This is а test” (Это тест)
выведет на дисплей строку ”This is а test” (Это тест). Закрывающая кавычка не является словом
Форта, это просто символ, который указывает слову .” на необходимость прекращения вывода стро¬
ки на дисплей. В языке Форт-79 слово .” рабютает как в определении нового слова, так и вне его, с
той лишь разницей, что вне определения строка выводится немедленно, в то время как, находясь
внутри определяемого слова, она не выводится, пока определяемое слово не будет исполнено. В вер¬
сии Форт-83 имеется незначительное отличие. Здесь не требуется, чтобы слово .” исполнялось вне
определения. Для немедленного вывода строки зарезервировано другое слово .((точка—скобка), ко¬
торое выводит символьную строку, ограниченную правой круглой скобкой.)
Оно работает так же, как слово .” в Форт-79 вне определения, немедленно выводя текст, однако
внутри определения .(так же выводит немедленно. Так, если ввести
TEST .(This is а test) ,
на экране будет немедленно выведена строка ”This is а test” (Это тест). Таким образом, слово
TEST в данном случае ничего не делает.
Упражнения
1. Определите слово BS (стирание влево) по аналогии со словом CR (возврат каретки) так, чтобы при исполнении слова
BS на выходе происходило бы стирание ранее выведенного предшествующего символа.
2. В MMSFORT есть слово PAGE (страница), которое производит стирание экрана и помещает курсор в левый верхний
угол. Определите слово PAGE, имея в виду действие управляющего кода 12 в MMSFORTH.
3. Определите слово CRS, которое должно перемещать строки на экране вверх на величину, определяемую числом в сте¬
ке, т.е. слово CRS должно действовать как возврат каретки вверх, так же, как слово SPACES действует над пробелами.
4. Определите слово DASHES (черточки), которое выводило бы на экран число черточек, которое задается числом в сте¬
ке.
66
5. Определите семь слов для вывода текста ”MAIN MENU” (основное меню). Теперь, пользуясь этим словом и словами,
определенными вами в упражнениях 2—4, определите слово MENU, которое выводило бы на экран следующее меню (не за¬
будьте о слове .”):
ОСНОВНОЕ МЕНЮ
А Это первый вариант выбора
В Это второй вариант выбора
С Это третий вариант выбора
D Это выбор Форта
чт0 ВЫ ВЫБИРАЕТЕ?
6. В MMSFORTH есть слово $.(вывод символьной строки), которое выводит на экран содержимое строки, если задать ад¬
рес счетной строки. Дайте определение слова $..
Управление экраном дисплея
Как вы, возможно, догадываетесь, в языке Форт не предусмотрены стандартные слова для управ¬
ления содержимым экрана дисплея; имеется множество других способов, с помощью которых ком¬
пьютеры и дисплеи осуществляют это. Однако мы увидим некоторые дополнительные возможности,
рассмотрев некоторые слова MMSFORTH, которые предназначены для работы с компьютерами типа
TRS-80 и IBM PC. Вы сможете определить такие же слова для вашей машины, если поймете, как
она исполняет управляющие коды, а возможно, в вашей версии Форта уже имеются подобные сло¬
ва. Наиболее важная функция управления экраном состоит в возможности перемещения курсора по
экрану. Если вы можете делать это, то сможете сделать почти все. Представьте себе экран как таб¬
лицу из символов с определенным числом строк и столбцов. Вам хотелось бы иметь возможность
поместить курсор в любую строку и столбец. В MMSFORTH для этого служит слово PTC (put
cursor — поместить курсор). Для него номер строки ожидается вторым в стеке, а номер столбца —
на вершине стека. Таким образом, 0 0 РТС передвинет курсор в левый верхний угол экрана, в то
время как 10 30 РТС поместит его в 11 строку и введет 31 пробел. Содержимое экрана не изменит¬
ся (кроме того, что на месте курсора и сообщения ”ок” прежние символы будут стерты). Указан¬
ную возможность можно рассматривать как форму, содержащую пустые места, которые должны
быть заполнены. Для примера, пусть меню задает вопрос, ответ на который должен начинаться в
строке номер 10 и столбце номер 30, а для текста вопроса отводится 10 пустых мест. Тогда слово,
которое поместит курсор и вставит в пустые места пробелы, выглядит так:
FILL-IN 10 30 РТС 10 SPACES 10 30 PTC , (Заполнигь)
Конечно, слово должно печатать вопрос вместо того, чтобы оставлять пробелы. Приведем другое
определение слова PAGE из последних упражнений, которое будет исполняться более медленно
(предполагается, что экран содержит 16 строк по 64 символа в строке, т.е. всего 1024 символа):
PAGE 0 0 РТС 1024 SPACES 0 0 PTC , (Страница)
Для экрана с 80 символами на строке и 25 строками число 1024 должно быть заменено на 2000
(80 * 25). (Очевидно, что скорость будет выше, если использовать какие-либо управляющие коды,
которые предоставляет компьютер или дисплей, например 12 EMIT в MMSFORTH.)
В гл. 12 и 13, где представлен и рассматривается экранный редактор, вы увидите, как может
быть определено слово РТС в других версиях Форта. Со словом РТС связано слово GTC (get_cursor,
т.е. определить, где находится курсор). Оно помещает в стек номер строки, затем номер столбца.
Приведем пример слова, подобного FILL-IN, которое поместит на экране после курсора 10 пробе¬
лов, если это возможно :
10BLANKS GTC 10 SPACES PTC , (10_пробелов)
Слово GTC выдает положение курсора, 10 SPACES перемещает его на 10 позиций вправо, запол¬
няя пробелами все, что находится на этих местах, и слово РТС снова возвращает курсор на старое
место, используя имеющуюся в стеке от GTC информацию о положении курсора. При небольшой
изобретательности слова РТС и GTC позволят вам делать впечатляющие и ’’дружественные” под¬
сказки для ввода в программы ответов пользователя, которому остается только вводить их на месте
пробелов. Можно практически обойтись без ’’перелистывания” экрана, если ввод и вывод информа¬
ции производить на одном и том же месте экрана.
Чем больше возможностей имеет дисплей компьютера, тем более специализированные слова
Форта требуются для использования этих возможностей. MMSFORTH совместно с IBM PC показы¬
3*
67
вают хороший пример того, что может быть сделано, но не будем их здесь описывать, поскольку их
много и они очень специфичны. Наиболее сложная возможность — это использование так называе¬
мых ”окон”. Они позволяют выделять на экране различные области, в каждой из которых появля¬
ются различные текстовые или графические изображения. Например, результат работы одной про¬
граммы может быть представлен в основном окне, а меньшее окно может быть выделено для вывода
листинга программы при отладке либо в одном окне можно помещать данные, вводимые в програм¬
му, а в другом — выходные данные. С помощью программы векторной графики MMSFORTH можно
задать несколько окон для отображения выходной графической информации, в то время как другие
окна могут быть оставлены для вывода алфавитно-цифровой информации. Цвет содержимого каж¬
дого окна и границ окон и даже цвет отдельных слов могут быть заданы независимо. Если в IBM не
установлена плата адаптера цветного монитора, тогда можно изменить вид курсора, ввести подчер¬
кивание и т.д. Этим достигается большая гибкость. Другие версии Форта могут обладать подобными
возможностями, но лишь немногие работают с окнами. (К примеру, очень впечатляет использова¬
ние окон MMSFORTH на ЭВМ Apple Macintosh.)
Вывод на печатающее устройство (принтер)
Стандарты Форта не определяют слова для осуществления вывода на принтер. Однако имеется
много возможностей для стандартизации. В Форте имеются способы передать выходную информа¬
цию на принтер или одновременно на принтер и на экран. Мы опишем, каким образом это реализо¬
вано в MMSFORTH. Почти наверняка в вашей версии Форта вы найдете эквивалентные слова. Уп¬
равление принтером реализуется с помощью слов PRINT, PCRT и CRT. После испоЛнения слова
PRINT вся информация, которая передавалась на экран, передается на принтер. После исполнения
слова PCRT вывод передается как на экран, так и на принтер, а после CRT прекращается вывод на
принтер, информация выводится на экран. Это так просто. Другой способ управления предоставля¬
ют слова PRINTER (принтер), SCREEN (экран), ON (включено), OFF (выключено), используемые
в контексте:
PRINTER ON (вывод_на_принтер)
SCREEN ON (вывод_на_экран)
PRINTER OFF (выключить_вывод_на_принтер)
SCREEN OFF (выключить_вывод„на_дисплей)
Указанные предложения производят несколько неожиданные действия. По идее это также должно
быть просто, но не торопитесь, так как все несколько сложнее. Что произойдет, если послать 12
EMIT при разрешенном выводе на принтер (PRINT)? Код 12 для большинства принтеров является
управляющим кодом, который заставляет принтер продвинуть бумагу на целую страницу (протяж¬
ка листа). А это, очевидно, не то же самое, что очистка экрана и возвращение курсора в левый вер¬
хний угол экрана. Или предположим, что вы посылаете управляющий код перевода строки назад
при активизации экрана командой CRT. Как отреагирует на этот код экран дисплея? До тех пор,
пока вы передаете алфавитно-цифровой текст и простой перевод каретки, никаких проблем с выво¬
дом не возникает. Но если вы делаете всякие замысловатые операции с дисплеем или принтером,
используя их управляющие коды, нужно быть очень осторожным при включении и выключении вы¬
вода на эти устройства, чтобы предотвратить нежелательную интерпретацию этих кодов. Это может
привести к дополнительным осложнениям.
Построение простейших графиков из линий
До появления в составе компьютеров хороших видеографических дисплеев и координатных по¬
строителей вывод графических данных приходилось делать на построчное печатающее устройство
или знаковый терминал. Действительно, вывод в такой форме и в настоящее время можно запрог¬
раммировать и выполнить гораздо быстрее, чем вывод в графическом виде. Идея того, как это дела¬
ется, очень проста, в особенности для Форта. Вы уже видели демонстрационную программу постро¬
ения линий в гл. 1. В данном разделе мы более подробно рассмотрим детали этого способа и, что
более важно, предложим вам несколько упражнений на построение линейных графиков. Допустим,
что вы хотите построить гистограмму , отображающую величины чисел, находящихся в стеке.
Прежде всего нам потребуется слово, которое делает высоту столбика пропорциональной величине
числа. Определим слово
■ XS 0 DO 88 EMIT LOOP , (выводить_"Х”)
68
печатающее ряд литер ”Х”, длина которого зависит от числа в стеке, это и есть наш столбик. Те¬
перь определим слово
: PL0T
(Нарисоватьграфик)
CR
(Начало с новой строки)
DEPTH
(Сколько чисел находится в стеке9)
0
(Нижний предел цикла)
D0
(Начало циклического повторения DEPTH
раз)
XS
(Печатает строку из n литер ’х’; n -
из стека)
CR
(Переходит в начало следующей строки)
L00P
(Конец цикла, конец определения)
Теперь, если вы напеча?аете 5 10 20 30 30 25 8 2 PLOT на экране дисплея, получится следую¬
щий график :
XX
xxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxx
xxxxx
Он должен быть вам знаком по гл. 1 и, конечно, не лишен недостатков. Числа должны быть по¬
мещены в стек в порядке, обратном тому, в каком они должны быть выведены. Длина столбика
должна быть не больше ширины экрана дисплея. Существуют, однако, пути обхода этих проблем и
способы построения более сложных диаграмм. С некоторыми мы познакомимся на следующих уп¬
ражнениях и в последующих разделах.
Упражнения
1. Измените слово PLOT (вызов: PLOTI) так, что если число больше ширины экрана, то столбик будет доходить только
до края экрана. (Указание: используйте слово MIN.)
2. Переделайте слово PLOT в PLOT2 так, чтобы вывод направлялся не на экран, а на принтер. По окончании вывода оно
должно переключить вывод снова на экран.
3. Измените слово PLOT в PLOT3 так, чтобы оно печатало пары значений чисел в стеке литерами X и Y одно под дру¬
гим, т.е. если в стеке находятся числа 12 10 8 5, то слово PLOT3 выведет на экран гистограмму
YYYYY
ХХХХХХХХХХ
YYYYYYYY
XXXXXXXXXXXX
Вам потребуется определить слово YS (литеры Y).
4. Измените слово PLOT в PLOT4 так, чтобы кроме вывода гистограммы оно выводило бы номера каждого столбика от 0
до 9 и между номером и началом гистограммы было три пробела. На выходе должна быть такая гистограмма
0 xxxxxx
1 XXXXXXXXXXXX
2 XX
3 xxxxxxxxxxxxxxxxxx
5. Измените слово упражнения 3 (назовите его PLOT5) так, чтобы вместо столбиков печаталась точка в конце каждого
столбика, т.е. таким образом :
0 X
1 X
2 X
3 X
И т.д.
6. Во'всех предыдущих программах построения гистограмм числа должны быть не больше числа позиций в ширину экра¬
на. Измените программу упражнения 5 на PLOT6 так, чтобы можно было задавать числа до 1000 и чтобы числу 1000 соот¬
ветствовал символ в 75-й позиции на строке экрана (считая, что 0 соответствует 1-я позиция). Если нужно, просмотрите
раздел о масштабировании в гл. 4. Не забудьте, что четыре позиции нужно предусмотреть для чисел и пробелов.
69
7. Измените слово упражнения 3 на PLOT7 таким образом, чтобы вместо печати столбиков друг под другом получалась
бы печать их рядом. Тогда на выходе должно быть
XXXXXXXXXXYYYYY
XXXXXXXXXXXXYYYYYYYY
8. Измените слово упражнения 7 на PLOT8, чтобы вместо столбиков печатались только их конечные точки, как показано:
X Y
X Y
9. Измените программу упражнения 8 на PLOT9, чтобы в конце каждой строки литер "Y” печаталась величина, как по¬
казано :
X Y5
X Y8
10. Измените программу упражнения 9 на PLOTIO, чтобы допустимыми были значения X от 0 до 30, а значения Y от 0
до 1000. Считайте, что в строке 64 символа.
11. Это трудное упражнение. Предположим, что у вас есть пары значений x и у, которые вы хотите отобразить в виде
графика зависимости у от x. Пусть значения x рассортированы так, что наименьшее значение находится на вершине Стека, а
пары значений в стеке расположены в порядке у, x. Например, в стеке может быть пара 30, 10, причем 30 — у, а 10 — x.
Теперь предположим, что в стеке имеются числа
30 10 20 7 15 5 10 4 2 1 10 .
При исполнении слова PLOT должна получаться следующая картина :
.X 0 1
. X 1 2
X 4 10
X 5 15
X 7 20
X 10 30
Таким образом, нулевое значение у должно быть представлено точкой, величина x должна быть отображена расстоянием
по направлению вниз, величина у — расстоянием от левого края и после каждой отображаемой точки должны быть напеча¬
таны значения x и у. Вам придется использовать вложенные счетные циклы типа DO-LOOP.
Вывод чисел
Вы уже знаете, конечно, как выводить числа на экран с помощью слов . (точка), U. и D., но это
очень примитивно. Предположим, что вы хотите представить таблицу, в которой числа были бы вы¬
ровнены по крайнему правому разряду, например:
1956 34215 343
23 5555 33333
21965 2 23
Или вы хотите напечатать число со знаком денежной единицы, например $294. Либо вам нужно
напечатать число с десятичной точкой (в известном месте, поскольку предварительно вы сделали
масштабирование), например, ”$294.32”.
Форт предоставляет превосходный выбор для отображения чисел в специальных форматах. Мож¬
но получить таблицу чисел с помощью печати, выровненной по правому краю. $294.32 можно напе¬
чатать, используя форматный вывод (иногда называемый выводом по шаблону). Печать в форме
таблицы делается просто, вывод со знаком денежной единицы — более дложно.
Слово R. позволяет печатать число в формате выравнивания вправо. Это означает, что число пе¬
чатается так, как будто бы его все время сдвигают вправо в поле некоторой определенной длины.
Длина поля должна быть положена на вершину стека. Так, если вы введете
CR 256 6 R
то увидите
256
(123456)
70
(цифры в скобках не будут напечатаны, они приводятся, чтобы показать позицию печатаемого чис¬
ла 256 в поле, отведенном для его вывода). Число 256 сдвигается на три позиции вправо и выводит¬
ся своим последним разрядом в последней позиции 6-местного поля. Если вы напечатали
CR 256 8 R
то увидите
256
(12345678)
Это значит, что число будет выровнено вправо в поле длиной 8 мест. Пусть теперь вы вводите
СП 211 10 .R -5 10 .R 23 10 .R CR 2 10 .R -231 10 .R 256 10 .R CR
Во всех случаях поле будет иметь ширину 10 позиций, выводимые числа будут выровнены впра¬
во в этом поле, и вы увидите
211 -5 23
2 -231 256
Каждое слово CR начинает вывод с новой строки, и каждое число представлено сдвинутым до
предела вправо в поле из 10 мест. В результате получается очень аккуратная таблица. Обратите
внимание, что знаки ”-” отрицательных чисел были учтены. Имейте в виду, что в отличие от .
(точки) слово .R не выводит пробел после напечатанного числа. Поэтому CR 256 6 .R 9 выдаст на
экран:
2569
(1234567)
Слово .R не оговорено стандартами Форта, но нам не известна ни одна версия языка, в которой
оно, равно как и слово D.R, отсутствовало бы. Во многих версиях Форта есть еще набор слов, как,
например U.R, которое печатает числа одинарной длины без знака с выравниванием вправо. Пред¬
полагается, что всем этим словам должна предшествовать на вершине стека длина поля в виде чис¬
ла одинарной длины, а ей должно предшествовать число, которое нужно напечатать как в случае
чисел одинарной, так и двойной длины. Если в вашем Форте нет слова U.R, то вскоре мы опреде¬
лим его вместе с другими полезными словами.
Форматный вывод чисел
Форматный вывод чисел (вывод по шаблону) наиболее сложный и все же наиболее гибкий спо¬
соб отображения чисел на экране дисплея или печати на принтере. Он используется также для оп¬
ределения таких слов, как . (точка) и .R, и позволяет вводить в число, например, знак денежной
единицы, десятичную точку, предшествующие незначащие нули, производить выравнивание в поле
и т.д. Слова форматного вывода фактически преобразуют числа в последовательность кодов ASCII в
форме, удобной для вывода на экран дисплея с помощью слова TYPE. Форматный вывод работает
только с числами двойной длины. Чтобы применить его к числам одинарной длины, последние не¬
обходимо преобразовать в двойные.
Процесс форматного вывода начинается словом <# и завершается словом #> . Между этими дву¬
мя словами могут быть какие-то другие слова, описывающие, как должно выполняться форматиро¬
вание. Слово #> оставляет в стеке адрес символьной строки и число символов в ней, подготавливая
вывод для слова TYPE. Простейшее фюрматирование производит слово #S, которое преобразует все
разряды числа в символьную строку. Приведем определение слова UD., которое, как ни удивитель¬
но, не включено во многие версии Форта:
UD ( d — )
<# ( — d ) (Начало форматного вывода)
#S ( — d ) (Преобразует все число в строку)
#> ( — адр счет ) (Завершает преобразование,
помещает в стек адрес и значение счетчика)
TYPE ( — ) (Выводит строку на экран)
SPACE (Вставляет после числа пробел)
; (Заканчивает определение)
А как обращаться с числами одинарной длины? Достаточно преобразовать их в числа двойной
длины. Для этого предусмотрено слово S-D. Чтобы понять его, вспомните, как представляется в
стеке отрицательное число:
S-D (n — d) DUP 0< IF -1 ELSE 0 THEN ; (одинарное_в_двойное)
Еще более изящно определяется это слово в Форт-79 :
: S-D (n — d) DUP 0< NEGATE ;
71
Если число одинарной длины в стеке отрицательное, то слово 0< возвращает 1, которую слово
NEGATE превращает в -1 в качестве старшей половины числа двойной длины. В случае, если число
положительное, 0< возвращает 0, который словом NEGATE никак не изменяется. Определением
слова S-D в Форт-83 будет
: S-D (n — d) DUP 0< ;
поскольку в Форт-83 0< возвращает -1, если число отрицательное, или 0 в противном случае (см.
гл. 7).
Приведем определение слова U. :
: U. ( n -- ) S-D <# #S #> TYPE SPACE ;
Так как слово #> возвращает число символов в символьной строке, мы можем определить также
слово UD.R:
(Печать_без_знака_с_выравниванием_вправо)
(Помещает значение длины поля на дно стека)
(Форматирует выводимое число)
UD.R
ROT ROT
d n — )
n d )
<# #S #> ( n адр счет )
ROT OVER ( адр счет n счет )
( адр счет пробелы )
( адр счет )
( )
SPACES
TYPE
(Помещает перед числом пробелы)
(Перемещает курсор на число пробелов)
(Выводит число)
; (Конец определения)
А вот определение для U.R :
: U.R (n n —) SWAP S-D <# #S #> ROT OVER - SPACES TYPE ;
Вы можете представить себе, как оно работает по аналогии с UD.R. Понимаете ли вы, почему
лучше было бы определить некоторое слово, которое использовалось бы как для определения слова
U.R, так и UD.R?
Как это ни удивительно, U.R, UD.R и UD. не включены в большинство версий Форта. Возможно,
вы пожелаете добавить их к своему Форту.
Как быть с печатью чисел со знаком? Для них нам потребуется слово SIGN (знак), которое встав¬
ляет знак — (минус) в символьную строку, если число, находящееся на вершине стека, отрицатель¬
ное. Если вы напечатали
1245. <# #S -1 SIGN #> TYPE
на экране появится число -1245.
Более полезно определение слова D., которое позволяет выводить числа двойной длины со зна¬
ком.
D.
( d
)
OVER ( ст# мл#
DABS
<# #S
ROT SIGN
#>
TYPE SPACE
SWAP ( мл# ст# — ст# мл# )
- ст# мл# ст# )
(Берет из стека абсолютное значение
двойного числа, оставляя старшую часть исходного числа на дне стека,
включая знак)
(Начинает форматирование)
(Перемещает старшую часть числа
ст# на вершину стека и помещает знак,
если он отрицательный, в начало строки)
(Заканчивает преобразование)
(Выводит число)
; (Конец определения)
Почему слово SIGN применяется после #S, а не перед ним? Потому что строка символов при
форматировании получается в результате просмотра справа налево. Это значит, что первым преоб¬
разуется наименее значащий (младший) разряд, поэтому после исполнения слова #S к строке сим¬
волов будет добавлен символ знака перед наиболее значащим (старшим) разрядом, где ему и следу¬
ет быть. Обратите внимание, что перед SIGN необходимо слово ROT, потому что слово #S не уда¬
ляет число из стека, а заменяет его числом 0 двойной длины. Процедуры форматного вывода еще
более неудобны для чисел со знаком. Поэтому для большего удобства желательно определить еще
два новых слова:
: <S# SWAP OVER DABS <# ;
чтобы заменить оператор <# для чисел со знаком и
: SSIGN ROT SIGN ;
используемое совместно с <S#. Тогда определение D. станет таким :
72
: D. (d --) <S# nS SSIGN tt> TYPE SPACE ;
Для полноты дадим еще определение слова . (точка) :
(n —) S-D <S# #S SSIGN tt> TYPE SPACE ;
Как оно работает, должно быть понятно. В большинстве версий Форта . (точка) определена как
операция форматного вывода, т.е. очень близко к этому определению. Мы обещали еще научить вас
выводить числа в формате с десятичной точкой и символами денежных единиц. Это делается с по¬
мощью слова HOLD, которое помещает символ ASCII, заданный его кодом, в символьную строку.
Так, например,
12.34 <tt ttS 36 HOLD tt> TYPE
выведет на экран $1234. Или
12.34 <# #S 37 HOLD #S #> TYPE
представит на экране 1234%. Снова обратим внимание на то, что строка формируется в обратном
порядке, т.е. в конец помещается символ, встречающийся первым, и наоборот.
Мы также обещали, что вы сможете вводить разделители, например десятичную точку, в выводи¬
мое число. Чтобы сделать это, вам потребуется другое слово #. Слово # выталкивает разряды чис¬
ла по одному справа налево и помещает их в выходную строку. После того как группа разрядов
преобразована, можно использовать #S для завершения преобразования. Попробуйте ввести
1234. <U tt tt 46 HOLD #S 36 HOLD tt> TYPE
и на экране должно появиться $12.34. Цифры 4 и 3 были помещены в выходную строку двумя сло¬
вами #, операция 46 HOLD поместила в строку десятичную точку, слово #S поместило в строку
оставшиеся два разряда 12, а 36 HOLD на вершину строки помещает знак денежной единицы $.
Снова обратите внимание, что процесс форматного преобразования происходит справа налево.
Теперь вы можете самостоятельно определить слово для представления чисел в долларах и цен¬
тах, и так как мы показали, что оно должно отображать числа со знаком, то с помощью этого слова
вы сможете записывать долги:
• D$ <Stt tt U 46 HOLD ttS SSIGN 36 HOLD tt> ,
Можно определить другие слова для более сложного вывода.
Здесь приведены все средства форматного вывода. Фактически форматирующими словами явля¬
ются только #S, SIGN, HOLD и #, но, как вы увидите из упражнений, они позволяют решать до¬
вольно сложные задачи форматного вывода. Следует напомнить, что между <# и #> могут исполь¬
зоваться любые ’’правильные” слова Форта. Прежде чем перейти к упражнениям, небезынтересно
рассмотреть, как работает слово #. Вот его определение: мы предполагаем, что в стек положено
число двойной длины 123, а содержимое стека представлено в десятичной системе, чтобы коммента¬
рии были более понятными :
: # (123 0) (Начало определения)
BASE @ (123 0 10) (Извлекает основание)
S-D (123 0 10 0) (Преобразует в число двойной длины)
D/M0D (3 0 12 0) (Остаток - это последняя цифра,
частное - остальная (непреобразованная)
часть числа)
R0T DR0P (3 12 0) (Превращает остаток в одинарное число)
R0T (12 0 3) (Помещает на вершину последний разряд)
48 + (12 0 51) (Добавление 48 преобразует число в код ASCII)
H0LD (12 0) (Помещает в строку код ASCII)
; (12 0) (Конец определения)
Процесс состоит в отделении разрядов от числа с конца (младшего разряда) по одному с по¬
мощью операции D/MOD, преобразовании каждого из них в код ASCII путем добавления десяти¬
чного числа 48 и, наконец, помещении кодов ASCII в символьную строку словом HOLD. Слово #S
можно определить через слово #, используя цикл, прекращающийся, когда частное станет равным
нулю; в этот момент все разряды числа будут отделены. Приводимое слово может выполнить все
указанные функции :
ttS BEGIN 0VER WHILE tt REPEAT
Поскольку вы еще не изучили конструкцию BEGIN...WHILE...REPEAT, это определение может
вам показаться или не показаться лишенным смысла, но главное, что делает #S:— повторяет опе¬
рацию # до тех пор, пока возвращаемое число двойной длины не станет равным нулю. Материал
гл. 8 сделает эту конструкцию более понятной.
73
Упражнения
1. Определите слово UD$. для печати чисел двойной длины без знака в формате долларов и центов, т.е. 1234. UD$. долж¬
но давать на экране $12.34 .
2. Определите слово US$, для печати чисел одинарной длины в долларах и центах.
3. Определите слово S$. для печати чисел одинарной длины со знаком в долларах и центах (включая, конечно, отрица¬
тельные числа).
4. Определите слово S$.R для печати чисел одинарной длины со знаком в долларах и центах, выровненных вправо в поле,
длина которого находится в стеке, и работающее как R..
5. Определите слово .L для печати чисел, выровненных влево. Числа со знаком, если таковые присутствуют, должны быть
сдвинуты влево, но курсор, который показывает позицию, куда должно помещать следующее число, должен переместиться в
поле вправо. Это значит, что после числа должно следовать некоторое количество пробелов.
6. Определите слово .DATE для форматного вывода даты. Так,
122388. DATE
на экране должно напечатать 12/23/88.
7. Определите слово .MDY, подобное .DATE, для печати даты в формате
m 12 d 23 у 88 месяц 12 день 23 год 88
8. Определите слово .PHONE (телефон), которое при вводе 824 959 2345. будет выводить номер телефона в формате
(824) 959-2345
9. Переменная #PT в MMSFORTH содержит в себе указатель места десятичной точки в последнем введенном числе двой¬
ной длины (считая справа налево). Определите слово FL. для печати числа с десятичной точкой в том же месте, что и во
введенном числе. Так, при вводе
12 34 FL
на выходе должно быть напечатано 12.34 , в то время как при вводе
1 234 FL
должно получиться 1.234. Воспользуйтесь счетным циклом DO-LOOP между <# и #>.
Ввод с клавиатуры
В Форте предусмотрено несколько слов для управления вводом с терминала. Некоторые из них
стандартные, другие — расширяющие возможности языка. Здесь мы рассмотрим наиболее простые
из этих слов.В Форте имеются достаточно мощные средства для определения других слов, управля¬
ющих вводом, однако вам будет проще с ними разобраться после того, как мы рассмотрим символь¬
ные строки в гл. 9. Дальнейшее изложение имеет целью дать вам необходимый на данной ступени
минимум.
Со словом KEY мы познакомились раньше. После ввода этого слова программа приостанавлива¬
ется, пока не будет нажата какая-либо клавиша, затем код ASCII выбранного символа помещается
в стек. Слово KEY удобно для того, чтобы выяснить код ASCII нажатой клавиши [ попробуйте вве¬
сти KEY. <BK>], но используется оно чаще всего для ввода одной литеры ответа на вопрос, тогда в
зависимости от значения кода клавиши программа будет выполнять какие-либо действия. В
MMSFORTH имеется родственное слову KEY слово и ?KEY. Оно обычно используется в цикле. Ес¬
ли ни одна из клавиш не нажата при очередном исполнении ?KEY в цикле, в стек кладется 0, если
клавиша нажата, то в стек кладется значение кода ASCII этой клавиши. Таким образом программа
выполняет какую-то задачу, пока не будет нажата клавиша, с этого момента программа будет вы¬
полнять другую задачу, в зависимости от кода клавиши. Как KEY, так и ?KEY позволяют осущест¬
вить выбор вариантов исполнения программы, но в то время как KEY ожидает ввода, слово ?KEY
позволяет продолжать исполнение программы, пока не будет нажата клавиша. Очевидно, слово
KEY может быть использовано в цикле для ввода строк символов, но более удобным для этой цели
является слово EXPECT. Для него необходимо, чтобы в стеке был адрес, начиная с которого будет
запоминаться символьная строка, и вторым в стеке — число символов, которое ’’ожидается”. Когда
встречается слово EXPECT, исполнение программы приостанавливается либо до ввода ожидаемого
числа символов, либо до нажатия клавиши <BK>. Испытайте действие приведенного ниже слова :
: WHATWORD ." Какое слово ? " PAD 10 EXPECT ;
Когда вы введете WHATWORD, исполнение программы приостановится после печати на экране
вопроса ”What is the word?” (Какое слово?). Теперь введите 10 букв, и, когда вы введете десятую
букву, исполнение программы продолжится. Теперь введите
PAD 10 TYPE
74
и вы увидите те 10 букв, которые были записаны, начиная с адреса PAD. А теперь введите с клави¬
атуры 5 символов и после этого нажмите <BK>, затем введите 10 PAD TYPE. Вы увидите 5 напеча¬
танных вами символов. To, что будет происходить далее, зависит от версии Форта, с которой вы ра¬
ботаете. Во всяком случае, <BK> прекращает действие слова EXPECT, т.е. ввод.
Слово EXPECT имеет дополнительные возможности, о которых вы узнаете из гл. 9. В
MMSFORTH имеется слово IN$, основанное на слове EXPECT, которое имеет аналогичное дейст¬
вие. Это слово ничего не ожидает из стека и помещает все, что было введено с клавиатуры, в стро¬
ку со счетчиком, начиная с адреса PAD, до нажатия клавиши <BK>, оставляя в стеке адрес PAD.
Введите IN$, потом <BK> , а затем строку символов и снова <BK>. Теперь если вы наберете
COUNT TYPE,
то увидите строку, которую ввели. Адрес PAD был оставлен в стеке словом IN$, слово COUNT кла¬
дет в стек длину строки, и TYPE печатает ее. Нестандартные слова IN$ и ему подобные являются
основными средствами для ввода текста в программу. Вы многое сделаете с их помощью в главе,
посвященной литерным строкам.
До сих пор мы рассматривали только ввод и вывод литер и литерных строк. А как быть с числа¬
ми? Конечно, можно записать их в стек с клавиатуры, перед тем как будет исполнено какое-либо
слово или начнет работать программа. Ну а что делать, если числа нужно вводить во время испол¬
нения программы? Это одна из наиболее слабых сторон стандарта Форт или по крайней мере одно
из неудобств для программиста. К счастью, в большинстве версий эта проблема решается с по¬
мощью специальных слов. В MMSFORTH такими словами служат #IN и D#IN. Оба они приоста¬
навливают исполнение программы и печатают на экране знак вопроса ?. Вводимые с клавиатуры до
нажатия клавиши <BK> символы воспринимаются затем как число либо одинарной, либо двойной
длины, в зависимости от слова, и помещаются в стек. Если используются недопустимые символы
или число имеет недопустимую длину, появляется сообщение ”Redo”(noBTopnTe), разрешая повто¬
рение ввода числа.
Попробуем ввести следующее определение :
■ TEST .” Какое число ” #IN ’’ Вы ввели ” .
Если в вашей версии слова #IN или эквивалентного ему нет, потребуется некоторая изобрета¬
тельность. Прием состоит в том, чтобы ввести литерную строку и затем преобразовать ее в число,
используя стандартное слово CONVERT (в некоторых версиях имеется эквивалентное ему слово
>BINARY). Достаточно мощное слово CONVERT работает только с числами одинарной длины без
знака, и применение его вызывает некоторые трудности. Вы еще убедитесь в больших возможно¬
стях слова CONVERT в гл. 9.
Выводы
Проблемы ввода и вывода показывают суть замысла стандартов языка Форт. Они предназначены
для обеспечения минимально необходимых средств для конструирования того, что вам может потре¬
боваться, но не оговаривают все детали и свойства, которые вы бы хотели иметь. Например, слова
форматного вывода, такие как EXPECT и CONVERT, в свою очередь, можно использовать для оп¬
ределения слов типа UD.R, $IN и #IN. Форт подвергается критике за небогатые возможности вво-
да-вывода, и, что касается стандартов, — это справедливо. Однако не представляет сложности доба¬
вить необходимые вам возможности, и в большинстве коммерческих реализациях языка они предус¬
мотрены. Вам нужно внимательно ознакомиться с документацией на тот вариант языка, которым
вы располагаете, чтобы узнать, что он может делать. Кроме того, в гл. 9 мы приведем определение
еще нескольких полезных слов.
Глава 6
ХРАНЕНИЕ ЧИСЕЛ В ПАМЯТИ
До сих пор числа, которые использовались Форт-программами, хранились в стеке. Если вы поработали с такими языками
программирования, как Бейсик, Фортран или Паскаль, это покажется вам достаточно странным. Большинство языков не ис¬
пользуют стек непосредственно, и числа в них хранятся в переменных. Переменные представляют собой ячейки памяти, ко¬
торые используются для хранения чисел, в большинстве языков предусматриваются размещение и автоматические манипуля¬
ции с числами в памяти. При этом стек фактически также используется, но программисту не приходится им управлять.
Рассмотрим следующую программу на Бейсике:
10 A=5
20 B=6
30 A=A+B
40 PRINT А
Число 5 запоминается в ячейке памяти, обозначенной как переменная А, число 6— в ячейке, отведенной для переменной
В. Когда значения А и В складываются, они на самом деле выбираются из памяти, складываются почти так же, как в Форте,
а затем результат снова помещается в ячейку, отведенную для переменной А. Хотя стек, может быть, и использовался для
операции сложения, программист и не подозревает об этом. Программа на Форте напечатает сумму чисел 5 и 6 следующим
образом:
5 6 + .
Все действия будут выполнены в стеке, в данном случае пользователю Форта не обязательно даже знать что-либо о пере¬
менных. Программирующий на Бейсике может привести доводы за использование переменных: программисту не приходится
следить, где находится число; в то же время программирующий на Форте будет возражать, что для переменных требуется
больше места в памяти, обращение с ними требует больших затрат машинного времени и текст программы получается более
длинным. Обе стороны правы, и, конечно же, любая большая программа где-то должна хранить числа, если они в данный
момент не используются. K счастью, Форт тоже позволяет вам пользоваться переменными (и соответствующими методами
хранения чисел), если вы предпочитаете их или они вам необходимы.
Какими же достоинствами обладают переменные, если вы можете обходиться стеком? Переменные служат для трех це¬
лей. Во-первых, если у вас имеется большое количество данных, которые нужно где-то сохранить, пока они не потребуются
программе. Во-вторых, с целью упрощения операций в стеке путем запоминания промежуточных результатов в виде пере¬
менных, а не в самом стеке. Наконец, в-третьих, для присваивания с помощью переменных имени числу или группе чисел.
Так же как переменные в алгебраических уравнениях, именованные переменные позволяют пользоваться математическими
абстракциями, с помощью которых облегчается формализация алгоритма программы, а написанную программу проще по¬
нять. Если у вас есть переменные с именами SECONDS (секунды) и MINUTES (минуты), то гораздо проще помнить, что
представляют собой эти числа, чем безымянные числа в стеке.
В более общей формулировке переменная представляет собой наименованную ячейку (адрес) памяти, которая может
содержать число. Переменная в Форте— это слово, которое возвращает в стек адрес, по которому может храниться число.
Для перемещения содержимого ячеек памяти, имеющих имя, в стек и обратно можно исгюльзовать слова @ (взять) и ! (за¬
нести), которые вам уже известны из гл.3. Мы рассмотрим сначала основные (но не самые удобные) способы для выполне¬
ния этих операций, а затем перейдем к более рациональным методам.
Создание переменных
Где можно надежно хранить переменные в памяти так, чтобы они случайным образом не измени¬
лись? Мы уже знаем, что Форт отводит часть памяти для своего словаря, в котором содержатся на¬
звания слов и их определения (в том смысле, что определения сообщают машине, какие операции
она должна делать при исполнении каждого слова). Мы имеем возможность создавать в словаре
слова, которые содержат числа. Когда такое слово исполняется, то единственное, что оно должно
делать,— это помещать в стек адрес числа, которое оно содержит, что обеспечивает доступ к нему с
помощью операторов ! и @.
Слово CREATE (создать) помещает на вершину словаря имя переменной и еще некоторую ин¬
формацию (которая, например позволяет Форту искать в словаре другие слова). Имя и эта инфор¬
мация представляют собой последовательность из нескольких байтов, называемую заголовком ука¬
занного слова. Когда исполняется слово, определенное словом CREATE, то в стек кладется адрес,
76
следующий непосредственно после адреса заголовка. Слово ALLOT (от ALLOcaTe— разместить) ре¬
зервирует определенное число байтов в словаре. Например,
CREATE INCHES 2 ALLOT
создает в словаре слово с именем INCHES (дюймы) и резервирует, или размещает, 2 байта, в кото¬
рых может быть помещено слово одинарной длины. Если вы введете
12 INCHES !
слово INCHES положит в стек зарезервированный адрес, а ! запишет в него число 12.
INCHES @ .
затем возвратит число 12 в стек и напечатает его. Это так просто. Между прочим, слово CREATE
имеет более сложные и изощренные применения, которые будут описаны в гл.14.
В этом месте мы совершим небольшое отвлечение в сторону, чтобы познакомиться с некоторыми
словами для перемещения чисел между памятью и стеком. Для всех таких слов предварительно
нужно задать адрес в памяти, с которым они работают. Вам уже знакомы слова @, !, C@ и С! .
Слова 2@ и 2! соответственно извлекают и запоминают числа двойной длины. В некоторых версиях
и в MMSFORTH определены также слова 4@ и 4! для работы с 4-байтовыми числами, с помощью
которых представляются числа с плавающей запятой. Несколько отличаются слова ? и +!. Слово ?
(обязательное в Форт-79, но не включенное в Форт-83) может быть определено просто как
. 9 ( addr — ) @ . ,
Оно извлекает число из ячейки памяти и печатает его на экране. Аналогично можно определить
слово С? (имеющееся в MMSFORTH и других версиях) для извлечения и печати одного байта.
Стандартное слово +! добавляет второе число из стека к числу, содержащемуся в ячейке с адресом,
который находится на вершине стека. Так, если в ячейке памяти с адресом 22345 содержится число
250, то
5 22345 +!
изменит содержимое на 255. Это равносильно следующим действиям :
22345 @ 5 + 22345 !
но выполняется значительно быстрее. Очевидно, что с помощью ALLOT можно резервировать место
для размещения более двух байтов.
CREATE INCHES 4 ALLOT
отводит место для хранения одного двойного слова, доступного с помощью слов 2! и 2@. Впрочем,
более важно то, что в отведенном (резервированном) месте могут храниться несколько чисел, вхо¬
дящих в одно слово. Если выполнить
CREATE YARDS 10 ALLOT
будет зарезервировано место, достаточное для хранения пяти одинарных слов (10 байтов). Пусть
теперь вы определили слова
!YARDS 2* YARDS + ! ;
И
. @YARDS 2* YARDS + @ ,
Тогда если ввести
23 2 !YARDS
то в четвертом и пятом байтах будет запомнено число 23 (если принять нумерацию с нулевого бай¬
та), зарезервированное словом YARDS. Операция 2 @YARDS возвращает в стек число 23. Другими
словами, вы можете зарезервировать место для хранения последовательности или списка чисел и
затем определить слова, с помощью которых к ним можно обращаться. Такой список чисел называ¬
ется массивом. Массив 1,3,45,671,23, каждое число которого называют элементом, называется од¬
номерным, линейным массивом или вектором. Можно хранить массив в слове YARDS следующим
образом:
1 0 !YARDS 3 1 !YARDS 45 2 !YARDS 671 3 !YARDS 23 4 !YARDS
а обращаться к числу можно с помощью слова @YARDS. Так,
3 @YARDS .
напечатает 671. По некоторым причинам многие считают массивы чем-то сложным и непонятным,
на самом деле это просто список чисел и ничего больше.
Слово , (запятая) упрощает создание переменных и массивов. Оно резервирует два байта так же,
как 2 ALLOT, а затем запоминает число, находящееся на вершине стека, в отведенных двух бай¬
тах. Поэтому
CREATE INCHES 7 ,
будет эквивалентно
CREATE INCHES 2 ALLOT INCHES 7 '
77
В обоих случаях будет создана переменная INCHES, которой будет присвоено значение 7. Слово
(запятая) особенно полезно при определении массива.
CREATE YARDS 1 , 3 , 45 , 671 , 23 ,
создает массив YARDS и инициализирует его содержимое гораздо проще, чем было показано рань¬
ше. В особенности оно полезно для создания таблиц данных, которые не должны изменяться. Нако¬
нец упомянем определенное во многих версиях Форта, и в том числе в MMSFORTH, слово C,. Оно
выполняет то же, что и , (запятая), но резервирует место и запоминает число в диапазоне 0— 255
только в одном байте. (Некоторые процессоры имеют адресацию памяти, не допускающую ис-поль-
зование слова C,.)
Упражнения
1. Создайте две переменные, FEET (футы) и INCHES (дюймы). Теперь определите слово F->T для преобразования числа
из FEET в число, выраженное в дюймах, результат должен помещаться в переменную INCHES.
2. Проделайте упражнение 1 для чисел двойной длины.
3. Дайте определение слова 2!, назвав его NEW2! , которое действовало бы как ! , но с числами двойной длины.
4. Дайте новое определение слова +! с именем NEW+!. (В Форте для ускорения оно определено в машинных кодах.)
5. Используя C@, дайте другое (но более медленно работающее) определение слова @, назвав его NEW@.
6. По аналогии определите слово NEW!, используя С! .
7. Определите слово VARSWAP (переставить переменные), которое должно переставлять содержимое двух переменных,
адреса которых находятся в стеке. Тогда
INCHES FEET VARSWAP
занесет содержимое переменной INCHES по адресу переменной FEET и наоборот. (Указание: используйте PAD.)
8. Определите два массива, из 7 элементов каждый, lWEEK (первая неделя) и 2WEEK (вторая неделя), пользуясь словом
, (запятая) для инициализации всех элементов нулями.
9. Определите семь слов !SUN(!ecKp), !МОЖ!пнд), ГГОЕ(!втр) и т.д. так, что, если им предшествует число (например,
сумма дневной выручки) и имя недели, они записывали бы это число в соответствующий элемент. Таким образом,
5 1WEEK !TUE
запомнит число 5 во втором элементе (вторник) массива lWEEK (первая неделя).
10. Напишите слова, соответствующие словам упражнения 9, которые способны извлекать содержимое переменных и
класть их в стек.
11. Определите слово ESWAP (переставить элементы), которое должно переставлять два элемента именованного массива.
Например,
1 3 1WEEK ESWAP
должно переставить значения для понедельника (MONday) и среды (WEdnesday) массива lWEEK (первая_неделя). Исполь¬
зуйте слово VARSWAP.
12. Создайте счетный массив однобайтовых элементов CNT (счетчик), в котором должно записываться, сколько раз числа
запоминаются в различных элементах массивов !WEEK и 2WEEK.
13. Определите слова +SUN, +MON и т.д., которые должны добавлять числа к соответствующим элементам именованных
массивов. Так, например,
7 1WEEK +THUR
должно добавить 7 к четвертому (THURsday— четверг) элементу массива lWEEK. Эти слова должны также добавлять по
единице к соответствующему элементу счетчика CNT.
14. Предположим, что все элементы массивов lWEEK и 2WEEK были инициализированы нулями и что изменение их со¬
держимого было произведено только с помощью операций +MON, +TUE и т.д. Тогда напишите слово DAY-AVE (сред-
нее_за_день), которое выдавало бы в стек среднее значение чисел, добавленных к конкретным элементам массивов lWEEK и
2WEEK. Так, если элемент 3 из массива lWEEK содержит 20, элемент 3 из массива 2WEEK содержит 30, а элемент 3 мас¬
сива CNT содержит 5, то выражение
3 DAY-AVE
должно выдать в стек 10.
Вы написали простую, но в то же время достаточно хитроумную программу, заслуживающую внимательного рассмотре¬
ния производимых ею действий. Набор задач был составлен так, чтобы в конечном счете получилась программа, которая оп¬
ределяет средние значения выручки за определенный день недели по двум неделям, причем как ежедневные поступления,
так и итоги за каждый день хранятся в двух массивах. В этом состояла наша цель. В большинстве языков программирования,
чтобы добиться поставленной цели, надо написать программу со всеми деталями и отладить ее как единое целое. На Форте
можно написать отдельные программы для каждой задачи и проверить их по отдельности. Разработка и написание програм¬
мы целиком со всеми деталями чаще всего по блок-схеме называется программированием сверху внизу и некоторые считают
такую методику обязательной. В противоположность этому Форт позволяет разрабатывать программу более гибко и экспери¬
78
ментировать с ней, и вследствие этого каждое слово или задача программируются и проверяются отдельно, чтобы решить не¬
которую часть общей задачи. В самом деле, вам не нужно было знать, приступая к упражнениям, какова их конечная цель.
Перемещение и заполнение содержимого массивов
Предположим, что имеется два массива по 20 элементов каждый, lDATA и 2DATA, и нужно
сделать так, чтобы содержимое массива lDATA было равно содержимому массива 2DATA. Можно
проделать это следующим образом:
1DATA 2DATA 20 CM0VE
Слово CMOVE ожидает, что в стеке должно быть два адреса и число (адр1 адр2 n — ), тогда оно
перешлет n байтов, начиная с адреса адр1, на адрес адр2. Причем сделает этй очень быстро. Первая
буква в слове CMOVE ассоциируется со словом character, т.е. литера, поскольку это слово часто ис¬
пользуется для пересылки байтов, представляющих литеры в литерных строках. Слово MOVE (пе¬
реместить), которое имеется в Форт-79 и большинстве реализаций, но отсутствует в Форт-83, дей¬
ствует так же, но пересылает указанное число ячеек, а не байтов. Таким образом,
10 M0VE
действует так же, как
20 CMOVE
Слово CMOVE обрабатывает байт за байтом. Это значит, что байт из ячейки с адресом адр1 пе¬
реносится в ячейку с адресом адр2, байт из ячейки с адресом адр1+1 переносится в адр2+1 и т.д.
Теперь рассмотрим случай, когда адр2 находится между адр1 и адр1+п, т.е. область, в которую
производится копирование, перекрывается с областью, откуда производится копирование. Теперь
допустим, что первый байт в массиве lDATA равен 219. Если напечатать
1DATA 1DATA 1+ 19 CMOVE
то число 219 будет скопировано из ячейки с адресом 0 в ячейку с адресом 1, затем из ячейки 1 в
ячейку 2, затем из 2 в 3 и, наконец, из ячейки 18 в ячейку 19. Другими словами, массив lDATA
будет целиком заполнен байтами со значением 219. А теперь предположим, что нужно установить
значения всех элементов массива равными 0. Можно сделать это с помощью CMOVE. Вам нужно
просто установить первый элемент массива равным 0, а затем скопировать его во все остальные
байты массива. Вот необходимая последовательность действий:
0 1DATA C' 1DATA 1DATA 1+ 19 CM0VE
Действие CMOVE, в данном случае неправильное, можно наглядно показать на диаграмме. Пред¬
положим, что слово CMOVE применяется к 4 байтам, содержащим в начале числа 10,11,12,13, и
оно перемещает байт с адресом 0 на адрес 1 и т.д. Процесс будет происходить так, как показано на
диаграмме
10 11 12 13
10 10 12 13
10 10 10 13
10 10 10 10
А нам нужно, чтобы из байта с адресом 1 число переместилось в ячейку 2; исходное число байта
с адресом 2— в ячейку с адресом 3 и т.д. без наложения байтов друг на друга. Это значит, что из
начального массива 10 11 12 13 должен в конце получиться массив 10 10 11 12 13. Слово, которое
позволяет сделать это, называется <CMOVE, оно имеется в большинстве версий Форт, но в Форт-83
оно называется CMOVE> (в Форт-79 оно необязательное). Слово <CMOVE ожидает в стеке три чис¬
ла (адр1 адр2 n — ), как и CMOVE, но оно начинает перемещение массива байтов с верхних эле¬
ментов, а не с нижних. Таким образом, диаграмма перемещения массива будет выглядеть так :
10 11 12 13
10 11 12 13 13
10 11 12 12 13
10 11 11 12 13
10 10 11 12 13 .
В данном случае перемещение элементов массива производится байт за байтом, но наложения не
происходит. Очевидно, слово <CMOVE можно использовать и для перемещения байтов в нап- рав-
лении уменьшающихся адресов памяти, но по-прежнему с байта, находящегося сверху.
Имеется также слово FILL (заполнить), которое заполняет некоторую область памяти байтами с
указанным значением. Так,
1DATA 20 0 FILL
79
заполнит 20 байтов нулями, начиная с адреса lDATA. Слово FILL используется, чтобы занести
число в определенное число байтов, начиная с некоторого известного адреса. В MMSFORTH n неко¬
торых версиях есть еще слово ERASE (стереть), которое заполняет память последовательностью из
нулей.
Таким образом, программа для предыдущего примера эквивалентна следующей:
1DATA 20 ERASE
Еще одно слово в MMSFORTH BLANK (пробел) заполняет область памяти кодами ASCII ’’про¬
бел”. Это равносильно
32 FILL
Очевидно вы сами можете дополнить приведенные слова собственными, имеющими специфиче¬
ское назначение.
Упражнения
1. Определите слово FILL (назовите его NEWFILL), используя слово CMOVE.
2. Определите слово ERASE (назовите его NEWERASE), используя слово FILL.
3. Определите слово INITIALIZE (инициализировать), чтобы установить все элементы массива чисел одинарной длины в
нуль, т.е.
1DATA 7 INITIALIZE
должно установить все семь элементов массива lDATA в нуль. Определение тривиально.
4. Напишите слово ARR-COPY (копировать массив), которое бы копировало содержимое массива одинарных чисел дли¬
ной n элементов в другой массив такой же длины, т.е.
1DATA 2DATA 7 ARR-C0PY
должно скопировать массив lDATA в массив 2DATA. Дайте два определения, используя как CMOVE, так и MOVE.
5. Напишите слово ARR-EXCH (обменять массивы), которое производит обмен содержимого двух массивов одинарных
чисел размерности n. Таким образом,
1DATA 2DATA 7 ARR-EXCH
перенесет массив lDATA в 2DATA и наоборот. (Указание: вос- пользуйтесь словами PAD и MOVE. Возможно, потребуется
вре- менно хранить где-либо количество чисел.)
Переменная, константа и связанные с ними слова
До сих пор в этой главе мы рассказывали вам о создании переменных окольным путем. Это дела¬
лось для того, чтобы вы привыкли думать о переменных как ячейках памяти, что представляется
неудобным, если вы знакомы с другими языками программирования. Существуют, однако, более
простые средства для обращения с переменными и массивами.
Стандартное слово VARIABLE (переменная) применяется для определения имени переменной и
резервирования под ее значе- ние двух байтов. Таким образом
VARIABLE INCHES
производит те же действия, что и
CREATE INCHES 2 ALL0T
но немного удобнее, что более важно: применение слова VARIABLE в программе вместо CREATE
делает ее более удобочитаемой и понятной. С переменной, которая определена, дейст- вуют так же,
как с переменной, созданной словом CREATE. (Напомним снова, что слово CREATE включено в
словарь Форта не только для того, чтобы создавать переменные, массивы и т.д. Оно также служит и
для других целей.)
Для обращения с двойными числами имеется стандартное слово 2VARIABLE. В некоторых реали¬
зациях Форта есть другие разновидности слова VARIABLE, например CVARIABLE для хранения
байтов, 4VARIABLE для переменных с плавающей запятой. Вам должно, быть понятно, что собст¬
венно слово VARIABLE (переменная) определяется следующим образом:
VARIABLE CREATE 2 ALLOT ,
Эта конструкция работает потому, что слово CREATE создает переменную при исполнении, а не
при компиляции, т.е. не во время добавления определяемого слова VARIABLE к словарю.
Массивы также могут определяться словом VARIABLE. Если ввести с клавиатуры
VARIABLE INCHES 8 ALLOT
то это будет равносильно
CREATE INCHES 10 ALL0T
80
причем вместо 8 используется 10, потому что два байта уже были резервированы словом
VARIABLE. С массивами, определенными любым из двух методов, можно обращаться одинаково.
Мы видим, что определение массивов как переменной словом VARIABLE несколько обескуражива¬
ет. Оно не имеет никаких преимуществ перед использованием слова CREATE и, кроме того, не де¬
лает различия между переменной и массивом, а программа воспринимается труднее.
Похожим на слово VARIABLE является слово CONSTANT (константа). Оно используется для
хранения таких чисел, которые внутри программы не будут изменяться (хотя иногда это правило
нарушается). Для него требуется число в стеке, а после слова CONSTANT— имя слова, в котором
будет храниться это число. При исполнении слова в стек кладется его содержимое. Поэтому после
слова, определенного как константа через слово CONSTANT, не требуется операция @ (извлече¬
ние содержимого). Например, если определить
5280 CONSTANT FT/MILE
то при исполнении слова FT/MILE в стек будет помещено число 5280. Слова, определенные с ис¬
пользованием слова CONSTANT, лучше всего применять для таких чисел, как коэффициенты пе¬
ресчета единиц измерения, константы уравнений. Конечно, с таким же успехом можно ввести чис¬
ло непосредственно в программу, но если программа подвергается изменениям, тогда каждое вхож¬
дение этого числа также должно быть изменено, в то время как при использовании константы чис¬
ло потребуется изменить только однократно. Кроме того, использование значащего слова вместо
числа способствует облегчению понимания программы. Если вы знакомы с языком Бейсик, то мо¬
жете подметить, что идея константы в языке Форт несколько отличается. В Бейсике константа
обычно рассматривается как число, включенное в выражение, например 56.5 в строке
90 А = 56 5 * В
В языке Форт константа— это именованная ячейка памяти, фак- тически это разновидность пе¬
ременной, которая посылает в стек не свой адрес, а содержимое.
Для переменных двойной длины в стандарте Форта предусматривается слово 2CONSTANT, во
многих версиях (в том числе в MMSFORTH) имеются слова CCONSTANT (однобайтовая констан¬
та) и 4CONSTANT для чисел с плавающей запятой. Смысл назначения константы— определять ве¬
личину, которая остается неизменной в программе, теряется, если константа изменяется. Вы долж¬
ны иметь возможность узнать значение константы из ее определения, не прибегая к поиску того
места, где она изменилась. Это абсолютно обязательно, если Форт-программа должна быть записана
в ПЗУ. Поэтому в большинстве случаев константа не должна изменяться. Но иногда бывают слу¬
чаи, когда необходимо изменить константу. Один из них возникает при отладке программы, другой
случай, когда константа по-разному используется в основной части программы и в другой. Наибо¬
лее важный случай, когда константа может быть изменена, если таким путем достигается сокраще¬
ние времени. Для помещения константы в стек требуется несколько меньше времени, чем для из¬
влечения значения переменной, и это нужно иметь в виду, если время исполнения программы для
вас становится критичным. Поэтому вам следует знать, как изменить значение константы. В стан¬
дарте Форт-79 и большинстве версий, не придерживающихся стандарта Форт-83, обязательным яв¬
ляется слово ’ (произносится ”тик”), которое кладет в стек адрес содержимого слова. Так, ’
FT/MILE выдает адрес ячейки памяти, где хранится число под именем FT/MILE, будь оно кон¬
стантой или переменной. Поэтому для изменения значения константы единственное, что нужно
сделать,— это поменять содержимое ячейки по этому адресу, например нужно ввести
6076 ’ FT/MILE '
чтобы изменить сухопутные мили на морские. Теперь, если ввести FT/MILE, в стек будет заслано
число 6076. (Заметьте, что точно такая же процедура (с излишними издержками) может приме¬
няться для изменения значения переменной.
В стандарте Форт-83 процедура сложнее, так как слово ’ возвращает не адрес содержимого ука¬
занного слова, а другой адрес, связанный с этим словом, что может привести к путанице (вскоре вы
поймете, что этот адрес полезен, но не для изменения константы). Однако если адрес, извлекаемый
словом ’, в стандарте Форт-83 известен, то слово >BODY даст нужный нам адрес, т.е. тот, который
извлекается в стандарте Форт-79 словом ’. Таким образом, при вводе
6076 ’ FT/MILE >B0DY !
в стандарте Форт-83 мы получим то же самое, что при вводе
6076 ’ FT/MILE !
в стандарте Форт-79. Дополнительное усложнение в Форт-83 состоит в том, что слово ’ работает,
как описано, только вне определения слов через двоеточие, т.е. оно является немедленно исполняе¬
мым. Все хорошо, если вы.хотите изменить константу, когда некоторая часть программы загружена.
81
Но если необходимо определить слово для изменения константы через двоеточие, то вместо слова ’
нужно пользоваться словом [’]. Так, например,
MAKE-NAUTICAL 6076 [’] FT/MILE >BODY ' ,
это определение слова, которое производит такое же действие, как слово, определенное следующим
образом :
: MAKE-NAUTICAL 6076 ’ FT/MILE ' ;
в стандарте Форт-79, потому что в данном случае не различается действие слова ’, стоит ли оно в
определении или исполняется непосредственно. Причины таких различий кроются глубоко в тонко¬
стях работы Форт-системы. Короче говоря, несколько слов вроде ’ в Форт-79 должны работать по-
разному в состоянии компиляции и исполнения, хотя кажется, что их действие одинаково. Такие
слова называют зависимыми от состояния. В стандарте Форт-79 разрешены слова, зависимые от со¬
стояния, в то время как в Форт-83 не разрешены, поэтому требуются два различных слова: одно
для состояния компиляции, другое для исполнения. Подробнее мы на этом остановимся в гл. 15.
Здесь, возможно, возникнет недоумение, зачем нам потребовались и константы, и переменные? По¬
чему бы не определить слово FT/FURLONG (футы_в_восьмую_часть_мили) таким образом, что
когда вы введете
660 FT/FURLONG '
то число будет запомнено, как в примере с FT/MILE. В то же время, если вы просто введете
FT/FURLONG, в стек будет выдано число 660 без операции @, как в случае константы. Если по¬
думать, то при этом слово FT/FURLONG должно вести себя двумя различными способами: одним,
когда оно используется само по себе, и другим, когда за ним следует операция @. В MMSFORTH
можно определять слова, в которых соединены свойства констант и переменных. Примерно таким
способом эта возможность может быть встроена и в другие версии.
В MMSFORTH имеется ключевое слово QUAN (от quantity— количество). Если ввести
QUAN FT/FURLONG
а после этого
660 IS FT/FURL0NG
то слово FT/FURLONG будет иметь свойства константы в том смысле, что оно возвратит число, ес¬
ли ввести слово. Но в то же время оно сходно с переменной, поскольку слово IS просто записывает
число из стека в FT/FURLONG. Если вы хотите узнать, где хранится это число, то предложение
AT FT/FURL0NG!
положит в стек его адрес. Таким образом,
660 AT FT/FURL0NG!
выполнит то же действие, что и
660 IS FT/FURL0NG
в то время как
AT FT/FURLONG @
равносильно тому, что вы введете FT/FURLONG. Предусмотрены также слова CQUAN (для бай¬
тов), 2QUAN (для двойных чисел) и 4QUAN (для чисел с плавающей запятой), назначение кото¬
рых очевидно. Кроме того, в языке предусмотрены также массивы QUAN и специальные слова типа
QUAN для хранения чисел с плавающей запятой сопроцессора типа 8087 и для работы с массивами
в расширенной памяти компьютера IBM. Уменьшение времени исполнения программы и требуемого
числа ячеек памяти благодаря слову QUAN по сравнению с использованием переменных происхо¬
дит, если QUAN применяется в программе более двух раз. Если в вашем распоряжении имеется
MMSFORTH, то дальнейшие детали вы найдете в его документации.
Упражнения
1. Определите слова CVARIABLE и 4VARIABLE.
2. Определите слово ARRAY, которое при исполнении будет определять массив переменных, состоящий из 16 различных
чисел, т.е. массив, с которым можно обращаться так же, как, например, с массивом, созданным предложением
CREATE FT/MILE 20 ALL0T
за исключением того, что он должен быть создан выражением
10 ARRAY FT/MILE
(Заметьте, что перед словом ARRAY в стеке должно стоять количество чисел, а не байтов, которое содержится в массиве.)
3. Определите по аналогии слова CARRAY, 2ARRAY, 4ARRAY.
4. Определите две переменные lLENGTH (длина) и 2LENGTH для помещения в них значений длины в разных едини¬
цах, например в сантиметрах и метрах или дюймах и футах. Определите константу (l->2), которая переводила бы содержи¬
82
мое переменной из lLENGTH в единицы длины переменной 2LENGTH, т.е. если в lLENGTH даны значения в дюймах, а в
2LENGTH в футах, то значение, которое выдаст слово l->2, должно быть равно "12. Напишите слово для преобразования со¬
держимого lLENGTH и запоминания его в переменной 2LENGTH. Как изменить программу, чтобы вместо дюймов и футов
слово для преобразования единиц длины работало бы с сантиметрами и метрами? Это покажет вам, насколько полезны кон¬
станты и почему им нужно присваивать значения при исходном определении.
5. Напишите слово с именем X->Y, которое находило бы значение Y, если в стек кладется значение X в соответствии с
уравнением Y - АХ + В , считая, что А и В предварительно определены как переменные. Понятно ли вам, как можно из¬
менить действие слова X->Y, не меняя его определения? Понятно ли вам также, почему использование переменных делает
программу более гибкой и удобочитаемой ?
6. Если слово INCHES (дюймы)— это переменная, в чем различие исполнения операций INCHES и ’ INCHES в стан¬
дарте Форт-79?
7. Предположим, что слово ТО-INCHES (в_^дюймы) представляет константу, в которой хранится коэффициент преобразо¬
вания единиц, т.е. в нем должно содержаться число 12, если требуется преобразование числа футов в число дюймов, и число
36,— если преобразование числа ярдов в число дюймов. В предположении, что вы пользуетесь стандартом Форт-83, дайте
определение двух слов SET-FEET (установитьфуты) и SET-YARDS (установитьярды) для записи соответственно чисел 12
и 36 в константу ТО-INCHES. Можно ли использовать слова ’ и >BODY?
О векторном исполнении операторов
Очень важным применением массивов (векторов) является управление программой для избира¬
тельного исполнения одного из нескольких возможных слов. Каждое слово имеет свой адрес, свя¬
занный с ним, который можно использовать для исполнения слова, фактически не вызывая его по
имени. Чтобы понять, как это делается, определим сначала слово
. MESSAGE ” Message” ,
и затем введем
FIND MESSAGE
Это слово определено в Форт-79; в Форт-83 перед словом MESSAGE нужно ввести ’ с клавиату¬
ры либо [’], если слово встречается в определении через двоеточие; слова ’ и [’] в Форт-83 действу¬
ют так же, как слово FIND в Форт-79. Слово же FIND в Форт-83 имеет совершенно другое назна¬
чение (см. гл. 14). Поэтому для Форт-83 нужно заменить слово FIND на ’ или [’] в последующем
изложении.
Если заглянуть в стек, то мы обнаружим, что в него был помещен некоторый адрес. Теперь, имея
этот адрес в стеке, если напечатать EXECUTE (исполнить), вы увидите на экране слово ’’Message”
(сообщение). Слово FIND (или ’ и [’] в Форт-83) возвращает адрес, который дает возможность сло¬
ву EXECUTE выполнить это слово. Таким образом,
FIND NESSAGE EXECUTE
производит то же самое, т.е. выдает на экран
Message
что делает слово MESSAGE само по себе. Это означает, что можно исполнить слово не только по
имени, но также косвенно с помощью FIND, находя в словаре его адрес и затем используя слово-
команду EXECUTE. При векторном исполнении применяется вектор (или переменная), в котором
содержатся адреса слов, найденные словом FIND. Затем соответствующий элемент засылается в
стек и слово EXECUTE исполняет нужное слово. Рассмотрим пример. Создадим массив из трех эле¬
ментов:
CREATE CHOICE 6 ALLOT
и теперь определим три слова
: 1PR0G .” Program 1" ,
. 2PR0G .” Program 2” ;
: 3PR0G ." Program 3” ;
Теперь запишем адреса этих слов в массив, вводя с клавиатуры
FIND 1PR0G CHOICE 0 + !
FIND 2PR0G CHOICE 2 + !
FIND 3PR0G CHOICE 4 + !
Если теперь введем
CHOICE 2 + @ EXECUTE
на экране появится сообщение ”Program2”, т.е. было исполнено слово 2PROG.
Предположим теперь, что вы хотите сделать выбор одной из программ lPROG, 2PROG или
3PROG, нажимая одну из трех клавиш А, В или С. Вы можете сделать это, вводя коды ASCII кла¬
83
виш с помощью слова KEY2 преобразуя затем их в числа 0, 1 или 2 соответственно, пользуясь кото¬
рыми можно выбрать и исполнить соответствующий элемент массива. Следующее слово выполнит
эту задачу:
: CHOOSE KEY 65-
2* CHOICE + @ EXECUTE ;
Конечно, нужно быть внимательным, чтобы ошибочно не нажать другую клавишу; слово
CHOOSE (выбрать) должно быть определено так, чтобы имелась защита от неправильного ввода.
Чтобы запомнить адрес слова, которое должно быть исполнено, вместо массива может быть также
использована переменная (в конце концов, переменная— это всего лишь массив из одного элемен¬
та).
Предположим, что вы хотите иметь одно слово, которое исполняло бы одну из нескольких раз¬
личных задач, например lTASK, 2TASK и т.д. Когда вы составляете программу, то не знаете точ¬
но, какие это будут задачи. Можно в самом начале программы объявить переменную, например
WHICH-TASK (какая_задача). Затем можно определить слово
: DO-TASK ( addr — ) WHICH-TASK @ EXECUTE ;
которое исполнит то слово, адрес которого записан в переменной WHICH-TASK. Впоследствии при
составлении программы вы определите lTASK, 2TASK и т.д. Теперь, если вы хотите, чтобы слово
DO-TASK (делать_задачу) исполнило задачу lTASK, нужно ввести
FIND 1TASK WHICH-TASK !
и слово DO-TASK— то же самое, как если бы вы ввели lTASK.
С другой стороны, если ввести
FIND 2TASK WHICH-TASK !
то тогда DO-TASK исполнит задачу 2TASK. Другими словами,
DO-TASK будет исполнять то слово, адрес которого находится в переменной WHICH-TASK. Вво¬
дом одного слова можно обеспечить выполнение одной из нескольких различных задач, в за- виси-
мости от значения содержимого переменной.
Некоторые Форт-системы очень широко применяют векторное исполнение, используя перемен¬
ную или константу, которая должна содержать исполнительный адрес базовых слов ядра Форт-сис¬
темы. Например, слово EMIT (вывести) может быть определено как
: EMIT (EMIT) EXECUTE ;
где слово (EMIT) является константой, которая содержит исполнительный адрес. Вы можете опре¬
делить теперь новое слово, например PRINTER (печать), которое в соответствии с вашим желанием
изменило бы действие слова EMIT (возможно,.определенное в машинных кодах), переводя вывод с
экрана на печатающее устройство. Тогда вы можете определить
(EMIT) CONSTANT SCREEN
чтобы сохранить старое определение слова EMIT. Теперь можно определить слова
: PRINTER-ON FIND PRINTER (EMIT) ! .
И
: PRINTER-OFF SCREEN (EMIT) ! ,
Когда исполняется слово PRINTER-ON, вывод будет идти на пе- чатающее устройство, а после
PRINTER-OFF — снова на экран.
Существует множество других способов выполнения подобного рода задач с помощью векторного
исполнения, которого мы едва коснулись. Вы найдете новые примеры в упражнениях, а более слож¬
ные его применения освещаются в книге Броди ’’Thinking FORTH” (1984).
Другое очень полезное применение массива, подобное векторному исполнению,— это поисковая
таблица, специализированной формой которой является таблица перекодировки. Предположим, что
вы пользуетесь терминалом, который неправильно понимает стандартные коды ASCII,T.e., напри¬
мер, забой влево, код которого 8, а на вашем дисплее это 22, вместо кода перевода строки 13 у тер¬
минала он равен 2, ваша программа посылает 1, чтобы очистить экран, в то время как терминал
для этой функции ожидает код 24.
Определим массив следующим образом:
CREATE TRANSLATE
24 С, 2 С, 3 С,
4
С,
5
С,
6
С,
7 С, 22 С, 9 С,
10
С,
11
С.
12
С,
23 С, 14 С. 15 С,
16
с,
17
С,
18
С,
19 С, 20 С, 21 С,
22
с,
23
С,
24
С,
25 С, 26 С, 27 С,
84
Теперь определим слово NEWEMIT, которое в вашей программе должно будет использоваться
вместо EMIT:
: NEWEMIT 1- TRANSLATE + C@ EMIT ;
Тогда вместо того, чтобы непосредственно выводить на экран коды, которые программа кладет в
стек, слово NEWEMIT будет просматривать в таблице TRANSLATE (перевести), каким новым уп¬
равляющим символом должен быть замещен символ из стека, прежде чем он будет выведен на тер¬
минал. Таблицы перекоди- ровки особенно удобны в применении к программам редактиро- вания
или управления терминалами, в которых управляющие клавиши могут по вашему желанию выпол¬
нять сложные специализированные функции.
Упражнения
1. Определите три слова, которые должны печатать "Dear Sir : ", "Dear Madame : " или "Dear Sir or Madame : ” (Глубо¬
коуважаемый сэр, Глубокоуважаемая мадам, Глубокоувджаемый(ая) сэр или мадам). Определите переменную, которая дол¬
жна содержать адрес одного из этих трех слов. Теперь определите слово SALUTATION (приветствие), которое печатало бы
один из трех вариантов приветствия, в зависимости от значения переменной. Предположим, что вы хотите изменить выводи¬
мый текст во время исполнения программы. Как это может быть сделано?
2. Вы пишете программу с меню, которое позволяет по выбору пользователя сделать вывод на экран, нажимая клавишу 1,
на печатающее устройство, нажимая клавишу 2, и на оба устройства одновременно, нажимая клавишу 3. В вашей Форт-сис¬
теме имеются слова PRINT (печать), PCRT (печать и экран), CRT (экран), определенные, как описано в гл. 6. Определите
слова, нужные для векторного исполнения, реагирующие на код нажатой клавиши в соответствии с меню. Вам нужно опре¬
делить массив из трех элементов и инициализировать его адресами для слов PRINT, PCRT и CRT, а затем определить слово
с помощью KEY и EXECUTE, которое исполняло бы одно из этих слов, когда нажималась клавиша 1, 2 или 3.
3. Оператор взвешивает болты и записывает их массу в компьютер. Оператору нужно узнать, сколько было болтов, имею¬
щих вес менее 100 г, сколько— вес от 100 до 200 г и сколько— тяжелее 200 г. Кроме того, он хочет знать общий вес болтов
каждой группы. Каждый раз после взвешивания болта оператор вводит его вес и затем слово BW (вес_болта). Для каждой
группы болтов имеются две переменные: COUNTl (счетчик1), COUNT2, COUNT3— для счета числа WT1 (вес1), WT2,
WT3— для общего веса болтов. Дайте определение этих переменных и инициализируйте их нулями:
а) определите слово CLASS (группа), которое выдавало бы 0, если вес болта меньше 100 г, 1— если он находится в ин¬
тервале 100-200 г и т.д, т.е. 125 CLASS должно выдать 1. (Указание: используйте оператор деления /.);
б) определите массив, который содержит адреса ячеек, где хранятся числа болтов, и другой массив, где хранятся адреса
суммарных весов. Заполните массивы соответствующей информацией. Эти массивы представляют собой поисковые таблицы;
в) теперь определите слово BW, которое оператор вводит после веса очередного болта. Это слово должно добавлять 1 в со¬
ответствующую переменную счетчика числа болтов и добавлять вес болта к соответствующей переменной суммарного веса.
Для него потребуется слово CLASS;
г) определите слово SUMMARY (итог), которое должно печа- тать количество, общий вес и средний вес в каждой группе.
Таким образом, при вводе 0 SUMMARY должно печататься количество, суммарный вес и средний вес болтов, весящих мень¬
ше 100 г.
4. Напишите программу, которая выполняла бы функции, заданные в упражнении 3, но на этот раз запомните число бол¬
тов в трехэлементном массиве COUNTS и веса в трехэлементном массиве WEIGHTS. Это будет более эффективно, чем
использование отдельных переменных. Можете ли вы объяснить, почему?
5. Теперь определите три слова, которые печатали бы
менее 100 г
от 100 до 200 г
больше 200 г
и поместите их адреса в массив, пригодный для векторного исполнения слов.
6. Определите три слова: CNT (счетчик), WElGHT (вес) и AVERAGE (среднее) и три константы SMALL (малый),
MEDIUM (средний) и LARGE (большой) так, что, если оператор вводит, например,
MEDIUM WEIGHT
на экране должно появиться сообщение
Суммарный вес болтов от 100 до 200 г равен 139
или
SMALL CNT
выведет
Число болтов весом меньше 100 г равно 33
и т.д.
Упражнения 3-6 иллюстрируют несколько принципиальных моментов:
85
1) Чтобы Форт-программа была полезной, не обязательно, чтобы она работала непрерывно. Чаще всего прикладные Форт-
программы лучше всего реализуются путем создания инструментального набора слов, которые могут быть использованы, как
в обычном калькуляторе. Так, например, слова BW и CNT, которые являются частью программы, но их исполнение проис¬
ходит только тогда, когда эти слова вводятся; в то же время массивы постоянно отслеживают соответствующие данные;
2) имеется несколько способов реализации отдельных частей программы. Можно использовать как массивы, так и пере¬
менные, чтобы запоминать количество и вес, однако если, как правило, имеется несколько различных групп данных, то бо¬
лее эффективно применение массивов;
3) векторное исполнение основано на использовании поисковой таблицы, в которой записаны адреса слов;
4) поисковые таблицы можно использовать для других целей, например для того, чтобы познакомиться с методами хране¬
ния чисел в переменных.
7. Вы пишете программу на языке Форт для обработки текста. После того как программа написана, вы решаете добавить
возможность работы оператора с клавиатуры Дворака вместо стандартной клавиатуры QWERTY (стандарт IBM). Клавиатура
Дворака показана на рис. 6.1.
Рис.6.1
а) напишите таблицу перекодировки, которая преобразует символы клавиатуры QWERTY в символы клавиатуры Двора-
ка, т.е. если, например, нажата клавиша К, то в стек должна посьшаться литера T;
б) переделайте слово KEY в DKEY, которое должно не просто принимать символ с клавиатуры и выдавать в стек его код:
DKEY должно воспринимать входной код как символ клавиатуры Дворака, находить его код в таблице и помещать его в
стек. Новое определение должно иметь примерно следующий вид:
‘ : DKEY KEY (Слова, с помощью которых производится просмотр);
В каком месте вашей программы будет использоваться новое определение DKEY?
8. Теперь вы решили ввести возможность работы оператора по выбору с клавиатуры Дворака или QWERTY. В начале
программы нужно ввести меню
Введите 1 для клавиатуры Дворака
Введите 2 для клавиатуры QWERTY
и в зависимости от того, какое число введено: 1 или 2 в переменную ?KBD, должен быть записан адрес, которым опреде¬
ляется тип используемой клавиатуры. Определите теперь слово NEWKEY таким образом, чтобы при вводе 2 оно действовало
бы так же, как и KEY, а если была введена 1, то производила бы просмотр таблицы и изменение значения кода как требует¬
ся.
Если эти упражнения показались вам трудными, не падайте духом! Они являются примером достаточно высокого уровня
программирования. Они также продемонстрировали вам, как можно делать то, что почти недоступно другим языкам
программирования. Вам следует внимательно разобраться в этих упражнениях и поэкспериментировать с применением ана¬
логичных приемов, чтобы хорошо прочувствовать возможности векторного исполнения и поисковых таблиц, когда они вам
потребуются.
Еще о массивах и матрицах
В MMSFORTH и некоторых версиях включено слово ARRAY (массив), которое упрощает обра¬
щение с массивами (это слово не является эквивалентом слова ARRAY, определенного нами выше).
В данном случае, если ввести
15 ARRAY DATA-STORE
86
вы организуете массив из 16 элементов с именем DATA-STORE (хранение_данных). Если затем
ввести
5 DATA-STORE
в стек будет положен адрес пятого элемента (считая с 0). Поэтому
293 5 DATA-STORE !
занесет число 293 в пятый элемент массива и
5 DATA-STORE @
выведет на экран число 293.
Слово ARRAY может быть определено в других версиях с некоторыми отличиями, поэтому нуж¬
но смотреть вашу документацию. Одна деталь может несколько смутить вас. В математике принято
нумеровать элементы массива, начиная с единицы, в MMSFORTH нумерация начинается с нуля.
Этим объясняется, почему
15 ARRAY DATA-STORE
создает массив из 16 элементов: как было сказано, номер последнего элемента равен 15, а так как
нумерация начинается с 0, всего получается 16 элементов. В MMSFORTH имеются также слова
CARRAY (байтовый массив) и DARRAY (массив чисел двойной длины), назначение которых оче¬
видно по аналогии с CVARLVBLE и 2VARIABLE.
Форт позволяет также пользоваться двумерными массивами, т.е. матрицами. Глубокое рассмотре¬
ние применения матриц выходит за рамки нашего рассмотрения; интересующимся рекомендуем
книгу ’’Essential Computer Mathematics” (Lipshutz, 1982). Однако здесь мы должны познакомиться с
ними вкратце.
Матрицы можно просто представлять себе как таблицу, содержащую некоторое число рядов и
столбцов. Рассмотрим, например, таблицу
5 293 1982
823 5 56
99 211 5
Она представляет собой квадратную матрицу размерности 3x3; на диагонали матрицы находятся
пятерки. Элементы матрицы обозначаются двумя индексами, показывающими их положение (стро¬
ка, столбец) следующим образом:
х1.1 Х1,2 Х1 , 3
х2’( 1 х2 , 2 х2 , 3
хЗ,1 х3,2 х3,3
Матрица может быть представлена в памяти как линейный массив. В данном случае имеется 9
элементов. Поэтому можно определить
CREATE МАТА 18 ALLOT
После этого программист должен взять на себя ответственность за установление связи между по¬
ложением элемента в строке и столбце и номером элемента в линейном массиве. Полезно иметь
универсальную формулу. Если обозначить положение элемента в строке через i, а в столбце через j
и за начальный (1,1) принять элемент, находящийся в левом верхнем углу, тогда номер элемента в
линейном массиве определится как
e = n(i-1) +j ,
где n — число столбцов в матрице. Таким образом, элемент (2,3) в матрице МАТА в линейном
массиве будет иметь номер 6. Учитывая это, можно определить слово для вычисления положения
элемента в матрице МАТА, если в стеке находятся значения i и j:
: MATAADR SWAP 1- 3 * + 1- 2* ;
следовательно, чтобы записать, например, число 1234 в элемент матрицы (3,1) , нужно ввести
1234 МАТА 3 1 MATAADR + !
Такова стандартная форма обращения с матрицами в Форте, однако в MMSFORTH и других вер¬
сиях имеются более удобные средства.
Слово 2ARRAY в MMSFORTH позволяет определить и организовать доступ к элементам матри¬
цы без дополнительных хлопот с обработкой индексов; будьте внимательны и не путайте слово
2ARRAY (двумерный массив, матрица) и DARRAY (линейный массив чисел двойной длины). Что¬
бы определить матрицу 2MATA размерности 3x3, нужно ввести
2 2 2ARRAY 2MATA
Числа, определяющие размерность матрицы, которые помещаются в стек, должны быть на 1
меньше числа строк p столбцов, так же как при создании линейного массива число в стеке должно
быть на 1 меньше его размерности. После того как матрица (в данном случае 2MATA) определена,
87
адрес ее элемента можно найти, вводя имя матрицы, если номера строки и столбца уже находятся в
стеке. Таким образом, чтобы напечатать элемент (2,3) матрицы 2MATA, нужно ввести
1 2 2MATA ?
Понятно ли вам, почему числа в стеке на 1 меньше фактических индексов элемента матрицы?
В MMSFORTH имеются также слова 2ARRAY и 2CARRAY. Их назначение очевидно. В гл. 11
будет рассмотрено, как можно определить такие слова.
Векторы и матрицы широко используются в разделе математики, который называется линейной
алгеброй и занимается решением систем уравнений. Они также широко используются в науке и
технике, в экономике для решения систем дифференциальных уравнений. В некоторых языках про¬
граммирования, например Фортране, АПЛ, фактически имеется целый набор стандартных функций
для работы с матрицами. Более сложные применения матриц выходят за рамки нашего обзора, од¬
нако есть множество задач повседневной практики, в которых полезно применяются матрицы,
обычно в том же виде, как для хранения табличных данных. Мы рассмотрим несколько таких при¬
ложений в следующих упражнениях.
Упражнения
Упражнения 1-4 похожи на то, что мы уже делали, тем не менее проделайте их для подготовки к упражнению 5, которое
показывает матрицу как таблицу. Считайте, что можно пользоваться словами MMSFORTH для работы с массивами.
1. Определите массив из 10 элементов под именем NUMBERS (числа). Теперь определите слово COUNT (счетчик), кото¬
рое должно добавлять единицу к элементу матрицы, в зависимости от введенного веса, находящегося в пределах 0— 100. Ес¬
ли вес от 1 до 10, то нужно прибавить к элементу 1, если вес от 9 до 20, то к элементу 2, если между 19 и 30— к элементу
3 и т.д. (Указание: используйте операцию деления /.)
2. Теперь определите второй массив, WTSUM (суммарный вес) и слово !W, которое будет добавлять вес к соответствую¬
щему элементу массива WTSUM так же, как числа добавляются к массиву NUMBERS.
3. Определите слова .TOT-#S (суммарное количество), .TOT-WT (суммарный вес) и .AVE-WT (средний вес), которые
соответственно будут печатать суммарное количество, общий вес и средний вес по группам в одной строке. Примените фор¬
матный вывод. (Используйте цикл DO, если же вы не уверены, что сможете это сделать, обратитесь к ответам.)
4. Определите слово !WT таким образом, чтобы каждый раз, когда оно исполняется, исполнялось бы слово !WT и при
этом суммарный вес, число деталей и средний вес по группам представлялись бы на экране друг под другом в виде таблицы
из трех строк и 10 столбцов.
5. Определите матрицу размерности 2x10, в каждом столбце которой содержится весовая группа, а в строках— общее ко¬
личество и суммарный вес соответственно. Теперь проделайте упражнения 1-4 снова, используя не линейный массив, а мат¬
рицу; при этом необходимо, чтобы все соответствующие элементы матрицы обновлялись после каждого исполнения слова !W.
6. Может оказаться очень удобным дополнительный стек. Создайте массив из 160 элементов с именем NEWSTACK, кото¬
рый вел бы себя как дополнительный стек. Теперь определите переменную STACKPOS (положение в стеке) для расчета те¬
кущего положения указателя стека, т.е. адреса вершины стека. Инициализируйте переменную в STACKPOS, чтобы она ука¬
зывала на первый элемент в NEWSTACK. Затем определите два слова: XPUSH и XPOP ; XPUSH (послать в стек) должно
брать число из обычного стека и засылать его в NEWSTACK, изменяя соответственно указатель. Слово XPOP должно изы¬
мать число из стека NEWSTACK и помещать его в обычный стек, вновь изменяя указатель. Модифицируйте слова XPUSH
и XPOP, назвав их PUSH и POP, используя слова MAX и MIN так, чтобы указатель не мог выйти за границы массива сте¬
ка.
7. Определите слова NEWDROP, NEWDUP и NEWSWAP, которые делали бы то же самое, что и слова DROP, DUP и
SWAP в обычном стеке.
О разном
Кроме переменных, создаваемых программистом, имеются различные переменные в самой Форт-
системе, например BASE (основание системы счисления)* Исторически переменные, которые явля¬
ются частью самого языка Форт, называются переменными пользователя, поскольку в многополь¬
зовательской системе каждый пользователь может иметь свой собственный набор этих переменных.
Мы понкмаем, что это не совсем удачное название, поскольку в большинстве языков программиро¬
вания переменная пользователя определяется им самим, а переменные, являющиеся частью самой
системы, например константа число п (3.1415...), называются системными переменными. Тем не
менее, следуя терминологии Форта, мы будем называть переменные, входящие в Форт-систему,
пользовательскими переменными. На самом деле пользовательские переменные это совсем не то
же самое, что переменные, которые определяет пользователь. Переменные, которые вы определяе¬
те, в качестве части своего определения могут включать число. Переменная пользователя содержит
88
число, которое может храниться в какой-либо удаленной от определения ячейке, и на практике не
нужно беспокоиться о том, где хранится эта переменная, потому что она ведет себя точно так же,
как и переменные, определенные программистом. Можно рассматривать переменную пользователя
как константу, значение которой представляет адрес, по которому хранится значение переменной.
Поэтому к ней применимы операции @ (извлечь содержимое) и ! (записать), как к обычным пере¬
менным.
Второй вопрос также относится к терминологии. Вы уже заметили, что некоторые слова языка
Форт используются только для определения других слов. К ним относятся : Двоеточие), УАЮАВЬЕ
(переменная), CONSTANT (константа), QUAN и ARRAY (массив). Такие слова называются опре¬
деляющими словами. Они применяются для составления программ. Слово CREATE (создать)— так¬
же определяющее слово, но это особое определяющее слово, потому что оно может быть использо¬
вано для создания других определяющих слов. Вы уже видели, как с помощью слова CREATE мож¬
но определить другое определяющее слово VARIABLE, которое, в свою очередь, используется для
определения других слов. Как вы увидите в гл. 11, применяя его совместно со словом
DOES>CREATE, можно полностью изменить характер работы вашего языка Форт.
Следует сказать несколько слов о стиле программирования. Если вы пишете программу, нужно
поделить ее на логические секции, каждая из которых имеет определенное назначение. Довольно
существенно определить заранее все константы, переменные и массивы, по крайней мере в начале
каждой программной секции, а еще лучше— в самом начале программы. Объявления констант и
переменных (так иногда называют их определение) группируются вместе, что облегчает внесение
исправлений и модификацию программы, если это необходимо.
Еще одно замечание о стиле программирования. Большинство новичков в Форте стараются ис¬
пользовать переменные неоправданно часто. Это в особенности присуще тем, кто знаком с языками
программирования, где переменные обязательны. Всегда при использовании переменной быстродей¬
ствие получается ниже, чем при использовании стека. И это одна из главных причин предпочти¬
тельного использования стека в Форте. Хотя может показаться, что переменными пользоваться про¬
ще, так как при этом не нужно следить за тем, что делается в стеке, за это приходится платить це¬
ной увеличения затрат памяти и времени.
Практически переменные (или константы и массивы) следует использовать для достижения одной
из следующих целей:
1) для размещения больших объемов данных, которые невозможно хранить в стеке из-за того,
что длина стека непомерно увеличивается;
2) для размещения чисел, которые используются неоднократно в разных сильно разнесенных сек¬
циях программы, или
3) для улучшения удобочитаемости программы.
Фактически есть еще одна причина для использования переменных. Если вы пишете программу,
которой воспользуетесь всего несколько раз и которая без введения переменных потребует очень
сложных манипуляций в стеке, то, может быть, использование переменных будет окуплено сокра¬
щением времени написания программы в ущерб времени ее исполнения. Но нужно сознавать, что
если это делать постоянно, то развиваются вредные привычки в программировании.
Выводы
Хотя в отличие от других языков программирования Форт применяет стек для большинства манипуляций с числами и пе¬
редачи аргументов из одного слова в другое, это не исключает использования переменных (а также констант и массивов),
как и в других языках. Форт также обеспечивает более глубокое управление этими средствами хранения данных, позволяя
создавать собственные конструкции с помощью слова CREATE. А с помощью слов типа CMOVE, ’ , FIND и EXECUTE мож¬
но делать то, что вообще невозможно в других языках программирования.
И в отличие от других языков переменные и массивы в Форте не только запоминают данные, но также сами управляют
программами и языком. Векторное исполнение программ дает мощное средство программирования, которого нет в других
языках. Например, переопределение слов EMIT или KEY равносильно тому, чтобы совершенно изменить действие операто¬
ров PRINT и INPUT в Бейсике. Это в некоторой степени должно объяснить вам, почему во введении мы говорили, что Форт
имеет большую мощность в управлении вашим компьютером, чем другие языки. Вы еще убедитесь в этом более ощутимо в
последних главах книги, где речь идет о создании новых определяющих слов и использовании Форт-ассемблера. Но пока мы
еще не рассмотрели множество более простых свойств языка.
Обязательной принадлежностью любого языка программирования являются управляющие структуры, т.е. процедуры типа
IF...THEN (если— то), которые позволяют принимать решения о дальнейшем ходе программы на основании определенных
условий. Мы рассмотрим такие управляющие структуры в гл. 7.
89
Глава 7
ОПЕРАТОРЫ СРАВНЕНИЯ И ВЕТВЛЕНИЯ
Одной из наиболее важных задач, которую должен уметь делать любой язык программирования высокого уровня, являет¬
ся выполнение некоторых операций на основании истинности или ложности некоторых условий. Например, если два верх¬
них элемента в стеке равны, должен быть выполнен один оператор, но если они не равны, то должно быть сделано что-то
другое. Такие условные операции, возможно, проще понять на примере из Бейсика, потому что их структура близка к есте¬
ственному языку. Если в выражении
100 IF A=B THEN X=Y ELSE GOSUB 300
переменные А и В равны, то значение переменной X устанавливается равным значению Y; в противном случае выполняется
некоторая подпрограмма, начинающаяся в трехсотой строке программы. Это позволяет программе выполнять разные дейст¬
вия при различных обстоятельствах. Подобные конструкции, которые управляют потоком (прохождением) программы, назы¬
ваются управляющими структурами; они включают в себя конструкцию IF...THEN , счетные циклы, которые вкратце уже
рассматривались, и другие средства для осуществления переходов в программе, рассматриваемые в данной и последующей
главах.
Флаг ( 0, ложно, не 0, истинно)
Слово 6
90
Рис.7.1
Выполнение,
если значение
флага истинно
Выполнение,
если значение
флага ложно
Слово 1
Слово 2
ELSE
Слово 3
Слово 4
THEN
В отличие от Бейсика конструкция IF...THEN сравнивает числа не в виде переменных, а в стеке и в Форте каждое слово
конструкции — это фактически подпрограмма. Конечно, отличается также и постфиксная форма записи. Поэтому условное
исполнение и ветвление в языке Форт записываются на языке Форт несколько по-другому. Приведенному выше выражению
на Бейсике в Форте будет эквивалентна следующая конструкция:
А @ В @ = IF Y @ X ! ELSE DOTHAT THEN
Конечно, на практике такое большое количество переменных в Форте никогда не используется. Рассмотрим приведенную
конструкцию более внимательно. Предложение А @ В @ извлекает значения двух переменных и кладет их в стек. Операция
- возвращает значение, зависящее от того, равны или не равны эти два числа (заметим, что - является оператором сравне¬
ния, а не присвоения, как на Фортране или в Бейсике). Если числа в стеке равны, то говорят, что условие истинно, тогда в
Форт-79, MMSFORTH и большинстве других версий в стек возвращается 1, а в Форт-83 — число -1 (т.е. 16-разрядное чис¬
ло, у которого все разряды равны 1, или FFFF в шестнадцатеричной системе счисления). Если числа не равны, то говорят,
что условие ложно, при этом во всех версиях Форта в стек возвращается 0. Величина, возвращаемая в стек оператором срав¬
нения, называется значением истинности, булевым флагом или просто флагом. В языке Форт любое ненулевое число всег¬
да считается истинным и 0 всегда ложным. В данном случае если перед IF находится истинное (ненулевое) значение, то ис¬
полняются слова, находящиеся между оператором IF и ELSE, слова между ELSE и THEN пропускаются и затем продолжа¬
ется исполнение той части, которая следует за словом THEN. Если перед IF находится ложное значение (0), исполнение пе¬
рескакивает на слово, которое следует после ELSE и продолжается до слова THEN.
На рис. 7.1 проиллюстрирована эта идея. Обратите внимание, что конструкции IF...THEN и IF...ELSE...THEN могут быть
использованы только в определениях через двоеточие. Вскоре мы более подробно обсудим конструкцию IF...ELSE...THEN, а
пока познакомимся с некоторыми другими операторами сравнения.
Проверка истинности
Теперь кратко остановимся на булевом флаге. To, что в Форт-83 (и других языках) оператор
сравнения возвращает значение флага -1, а не 1, имеет определенное основание. При обнаружении
истинности условия число -I или шестнадцатеричное FFFF, т.е. содержащее во всех разрядах еди¬
ницы, оказывается иногда более удобным для использования его с булевскими операциями. Предпо¬
ложим, например, что требуется заменить число нулем, если флаг имеет значение ложь, и оставить
без изменения, если флаг имеет значение истина. Если числа находятся в стеке, причем флаг на
вершине, то оператор AND (”И”) выполнит эту задачу в Форт-83, но не в Форт-79, для которого
потребуется конструкция
0 = IF DROP 0 THEN
Хотя подобные случаи не так уж часты, иногда программа может дать выигрыш по времени,
пользуясь этой особенностью флага. (При необходимости вспомните действие оператора AND в гл.
3.) Если посмотреть с более общих позиций, то в Форт-83 значение истина имеет не только сам
флаг, но и каждый его разряд, что может оказаться полезным при операциях поразрядного сравне¬
ния. Обратимся теперь к операторам сравнения; слово = (равно) — только одно из нескольких
предусмотренных в языке Форт. Имеются также операторы сравнения для одинарных чисел и чисел
двойной длины со знаком и без знака. В табл. 7.1 приводится сводка этих операторов. Большинство
из приведенных операторов в комментариях не нуждаются. Некоторые особенности имеют операто¬
ры U< (и по аналогии DU<). Если ввести
60000 20000 <
то в стек будет возвращено значение истина, потому что операция < обнаружит не 60000, а отри¬
цательное число.
Операторы U< и UD< применяются для сравнения больших чисел, чтобы они не рассматривались
как отрицательные. Как и для арифметических операций, при использовании чисел без знака тре¬
буется некоторая осмотрительность.
Особого внимания заслуживает оператор 0=. Он всегда меняет результат сравнения на обратный.
Таким образом, число, не равное нулю, превратится в 0, в то время как 0 превратится в 1 (или в -1
в Форт-83). Для повышения удобочитаемости программ в Форт-79 слово 0= имеет стандартный си¬
ноним NOT (не), который действует аналогичным образом. (В Форт-83 оператор NOT действует
по-другому, как было описано в гл. 3.) Если целью сравнения является выяснение того, действи¬
тельно ли в стеке находится нуль, то более оправдано применение оператора 0=, в то время как
оператор NOT уместнее, если целью является изменение значения истинности на обратное. Заме¬
тим, что, если в Форте нет слова <>, его можно заменить конструкцией = NOT или = 0=. Точно
так же оператор - (минус) будет возвращать значение 0 или не 0, как оператор <>. Запомните, что
операторы сравнения снимают числа из стека. Поэтому, если вам потребуются эти числа для даль¬
91
нейшей работы, их необходимо скопировать в стеке. Обычно это делается с помощью операций
OVER OVER или 2DUP.
Таблица 7.1. Операторы сравнения*
Имя Операнды Определено ли Результат
слова в стеке стандартом? (возвращаемое значение)
=
n1
п2
Да
истина,
если
n1 = n2
<>
n1
n2
нет
истина,
если
n1=/=n2
<
n 1
n2
да
истина,
если
n1 < n2
>
n1
n2
Да
истина,
если
n1 > n2
<=
n 1
n2
нет
истина,
если
n1 <= n2
>=
n1
n2
да
истина,
если
n1 >= n2
0=
n
да
истина,
если
n = 0
0<
n
да
истина,
если
n < 0
0>
n
да
истина,
если
n > 0
D=
d1
d2
да
истина,
если
d1 = d2
D<
d1
d2
да
истина,
если
dl < d2
D0=
d
Да
истина,
если
d = 0
U<
u1
u2
да
истина,
если
u1 = u2
DU<
ud1 ud2
да
истина,
если
ud1=ud2
* Нестандартные слова включены в MMFORTH.
Прежде чем продолжить рассмотрение применения операторов сравнения, познакомимся с логи»
ческими операторами AND, OR и XOR. Они позволяют комбинировать несколько условий. Пусть,
например, вы хотите выполнить какую-то операцию только в том случае, если для переменных А,
В и С выполняются условия A=C и B=C.
А @ С @ = В @ С @ = AND IF...
Если обе пары переменных (первая и вторая) равны, то оператор AND, обнаружив в стеке 1 1
(или -1 -1), возвратит в стек 1 (или -1). Если одна или обе пары не равны, в Стеке будет по край¬
ней мере один 0, тогда оператор AND возвратит 0. Аналогично используется логический оператор
OR. Допустим, что вы хотите выполнить какое-то действие, если или A=C, или B=C, или A=B=C.
Это можно сделать следующим образом:
А @ С @ = В @ С @ = OR IF ..
Если либо одно, либо другое равенство (либо оба) истинны, тогда по крайней мере одно условие
истинно, поэтому оператор OR обнаружит в стеке хотя бы одну 1 (или -1, т.е. число со всеми раз¬
рядами, равными 1) и выдаст в стек значение истина. Если оба равенства ложны, то оператор OR,
увидев в стеке два нуля, возвратит в стек значение ложь (нуль). Наконец, пусть необходимо, чтобы
какое-либо действие выполнялось только в том случае, если одно равенство выполняется, а другое
нет. Это можно сделать так :
А @ С @ = В @ С @ = XOR IF...
Оператор XOR возвращает в стек значение истина, если в стеке есть флаги истина и ложь (раз¬
ряды в одном числе установлены в 1, а в другом не установлены). Комбинируя операторы сравне¬
ния с логическими операторами AND, OR и XOR, можно выполнять всевозможные комбинации
сравнений. Если вы знакомы с булевой алгеброй и диаграммами Венна, то представляете, как изо¬
бражать на них всевозможные сочетания условий для облегчения реализации большого разнообра¬
зия условного исполнения программ в комбинации с конструкцией IF...ELSE...THEN,
Упражнения
1. Пусть в версии языка, с которой вы работаете, есть только один оператор сравнения <. Проделайте в предлагаемом по¬
рядке следующее. Определите слово >. Определите 0<. Затем определите <- (используйте слово -). Определите >-. Опреде¬
лите - . Определите <>. Определите frr Все введенные вами определения снабдите префиксом NEW.
2. Пусть числа а, b, с и d находятся в стеке в указанном порядке. Напишите последовательность слов, которая выдавала
бы флаг истшш тогда, и только тогда, когда выполняются следующие условия:
а) a=b И c=d б) a=b ИЛИ C=d
92
в) a=b И c<>d г) a=b ИЛИ c<>d
д) a<>b И c<>d e) a=b ИЛИ c=d, но не одновременно
ж) a>b И c<d з) a=b=c
и) а > b > с .
3. Используя слово MOD, дайте определение слова 7REM4) (остаток равен 0?), которое возвращает флаг истина тогда, и
только тогда, когда второе число в стеке делится без остатка на число, находящееся на вершине стека.
4. Дайте определение слова ?REM, которое будет возвращать в стек флаг ложь при условиях упражнения 3 и истина в
противном случае.
5. Определите слово ?OPPOSITE (?противоположные), которое возвращало бы флаг истина тогда, и только тогда, когда
оба числа по модулю были бы равны, но имели противоположные знаки. Определите слово NEW-, используя только 0- и
арифметический оператор.
6. Определите слово D- , пользуясь только арифметическим оператором и словом ^-.
7. Пользуясь соглашениями Форт-79, определите слово COMP, которое будет возвращать -1, если число отрицательное,
0, если оно равно 0, и 1, если оно положительное, не применяя конструкции IF.
Операторы IF, ELSE и THEN
Операторы сравнения мало полезны без других операторов, которые реагируют на значение фла¬
га, возвращаемого в стек операторами сравнения. Наиболее важным из наших условных операто¬
ров является слово IF, используемое в конструкции IF...ELSE...THEN, рассмотренной в начале этой
главы. Другие важные слова, реагирующие на флаг проверки условия, это UNTIL и WHILE, при-
меняемые в конструкциях BEGIN...UNTIL и BEGIN...WHILE...REPEAT; эти конструкции позволя¬
ют зацикливать программу до тех пор, пока значение флага есть истина или ложь. Слово BEGIN и
связанные с ним слова мы рассмотрим более внимательно в гл. 8.
Еще два условных оператора, имеющихся во многих версиях Форта, это ?DUP и ABORT”, к ним
мы еще вернемся в этом разделе. Действие конструкции IF...ELSE...THEN можно в сущности пред¬
ставить следующим образом (см. также рис. 7.1) :
( флаг ) IF ( число в стеке не равно нулю,
исполнить слова, стоящие здесь)
ELSE ( если нуль, исполнить эти слова)
THEN ( в любом случае продолжить отсюда)
Слово ELSE не является обязательным и часто опускается. Если оно отсутствует, то остается
только одна возможность: исполнить слова, находящиеся между IF и THEN, если флаг, который
оператор fF видит в стеке, является истинным. Со словом ELSE имеются две возможности : испол¬
нить слова, находящиеся между IF и ELSE, если флаг имеет значение истина, или слова, заклю¬
ченные между ELSE и THEN, если флаг имеет значение ложь. В обоих случаях исполнение про¬
должается после слова THEN. Лучше всего можно понять эти идеи на примере.
В гл. 6 вы познакомились с программой, в которой оператор вводил вес болтов, а затем слово
WT. После этого число классифицировалось по величине и изменялись соответствующие перемен¬
ные. Допустимый вес находился в диапазоне 0 — 300 г. Предположим, что вы хотите предотвратить
ввод оператором неверного числа, которое или очень велико, или мало. Можно сделать это, прове¬
рив величину числа с помощью операторов сравнения, а потом принять это число либо выдать сооб¬
щение об ошибке в соответствии с конструкцией IF...ELSE...THEN. Чтобы осуществить это, пере¬
именуйте прежнее слово WT в (WT) (заключением слова в скобки чаще всего подчеркивают, что
оно является частью другого слова со сходным названием) и определите новое слово следующим об¬
разом:
. WT DUP 0< 0VER 299 > 0R 0=
IF (WT)
ELSE DR0P .” Вышли за пределы диапазона”
THEN ;
Заметим, что необходимо включить ELSE DROP для исключения неверных данных из стека пе¬
ред словом (WT). Слово WT мoжнo немного упростить, убрав оператор 0= следующим образом:
: WT DUP 0< 0VER 299 > 0R
IF DR0P ." Вышли за пределы диапазона"
ELSE (WT)
THEN ;
93
Вообще слово 0= перед конструкцией IF...ELSE...THEN вовсе не требуется, так как точно такого
же результата можно добиться, переставляя слова между IF и ELSE со словами, находящимися
между ELSE и THEN.
Конструкции IF...THEN могут быть вложенными, т.е. их можно использовать внутри таких же
конструкций. В слове WT мы классифицировали значения, пользуясь делением на 100 (/) и после¬
дующим векторным исполнением. Можно сделать то же, применяя оператор IF. Пусть, если вес
меньше 100 г, должна выполняться задача lTASK, если вес от 100 до 200 г — задача 2TASK, и за¬
дача 3TASK, если вес от 200 до 300 г. Можно сделать так:
: (WT) DUP 100 < IF 1TASK ELSE
DUP 200 < IF 2TASK ELSE
DUP 300 < IF 3TASK
THEN THEN THEN DR0P ;
Метод векторного исполнения с операцией деления на 100 может показаться более изящным, но
он не намного быстрее и, кроме того, не будет работать, если разбросы веса в каждой группе неоди¬
наковы. Заметьте, что в данном случае мы использовали как IF...THEN, так и IF...ELSE...THEN
конструкции. Первые два оператора IF нужно использовать с ELSE, потому что, если условия лож¬
ны, число должно переходить в следующий класс с большими значениями веса. А так как слово
(WT) является частью слова WT, третий оператор IF используется без ELSE, потому что число
больше 300 быть не может. Немного дальше мы покажем, что можно также применить конструк¬
цию выбора одной из нескольких возможностей (переключатель).
Некоторые программисты строго придерживаются манеры выделять вложенные операторы
IF...THEN, подчеркивая их вложенность, поэтому запись определения (WP) в предыдущем примере
вызовет у них возражение, так как она плохо сформатирована, и они отдадут предпочтение такой
форме записи:
: (WT) DUP 100 <
IF 1TASK
ELSE DUP 200 <
IF 2TASK
ELSE DUP 300 <
IF 3TASK
THEN
THEN
THEN DR0P
Решайте сами, стоит ли ради наглядности программы занимать больше места на диске.
Вот еще один пример. Пусть нужно выполнить операцию DOIT, используя остаток от деления на
22 только тогда, когда число в стеке не кратно 22, т.е. если
n 22 M0D
возвратит в стек ненулевое значение. Вот как это можно сделать :
: 722-MULTIPLE 22 M0D DUP IF D0IT ELSE DR0P THEN ;
Часто функция оператора IF может быть успешно выполнена без оператора сравнения. В этом
случае что-то должно быть сделано или не сделано в зависимости от того, находится ли в стеке
нуль или не нуль. Сочетание ELSE DROP необходимо для того, чтобы убрать из стека нуль, по¬
рожденный оператором DUP. В подобных случаях очень полезно слово ?DUP, поскольку оно кладет
в стек копию числа только в том случае, когда оно не равно нулю, заменяя два слова ELSE DROP.
Вот, например, короткое определение слова 722-MULTIPLE (кратно_ ли_ 22?):
. 722-MULTIPLE 22 M0D ?DUP IF D0IT THEN ;
Если в стеке находится нуль, слово ?DUP ничего не выполняет, следовательно, применяя его, мы
избавляемся от заботы очищения стека от оставленного нуля. ?DUP не дает никакого выигрыша,
если перед оператором IF стоит оператор сравнения. Но если перед IF следовала арифметическая
операция и при ненулевом значении результата должна быть выполнена другая программа, тогда
использование ?DUP экономит время, особенно в цикле. Применение оператора MOD совместно с
IF весьма полезно в циклах DO-LOOP, когда нужно что-то выполнить с определенным интервалом.
Например,
: VMULTIPLES? 1+ 0 SWAP 1 D0 I 7 M0D 0= IF 1+ THEN L00P
. . ’’ раз число 7 содержится в данном числе " CR ;
при исполнении
38 7MULTIPLES?
выведет в результате
94
5 раз число 7 содержится в данном числе
Хотя данный пример тривиален, в нем показан прием, который пригодится для составления более
сложных программ, чем определение кратности одного числа другому.
Некоторые замечания o структурном программировании
Переход на исполнение определенных операций (т.е. подпрограмм) является одним из наиболее
важных механизмов языка программирования. В таких языках, как Бейсик, допускается переход в
любое место программы, куда вы хотите, не обязательно к четко оформленной задаче или подпрог¬
рамме. И возврат не обязательно должен происходить в точку, из которой произошел переход. Опе¬
ратор Бейсика
GOTO nnn
где nnn — номер строки, позволяет делать переход в программе произвольно, с возвратом или без
возврата в исходную точку, как вам кажется удобнее. Это потенциально может привести к запутан¬
ной программе, в которой трудно проследить ход действий, что затрудняет ее отладку и модифика¬
цию.
Альтернативным методом является структурное программирование. Упрощенно структурное
программирование заключается в том, что программа составляется таким образом, что когда
она уходит на подпрограмму, то ее исполнение затем продолжается с точки, следующей сразу
же после точки ухода на подпрограмму, и в подпрограмме имеется только одна точка входа.
Язык Форт поощряет структурный подход в программировании, так как любое слово Форта по су¬
ществу является подпрограммой, у которой обычно имеется только один вход и один выход. Другие
языки, например Паскаль или Модула-2, также обязывают к применению структурного программи¬
рования ввиду того, что вход в подпрограмму может быть произведен с помощью вызова именован¬
ной процедуры, возвращающей управление в точку, из которой она была вызвана. Это обстоятель¬
ство может показаться неудобным, потому что каждый переход на подпрограмму требует самостоя¬
тельной именованной процедуры. Форт в этом отношении обладает большей гибкостью. Поскольку
все, что делается на языке Форт, исполняется подпрограммами, т.е. словами-операторами Форта,
эти программы структурные по своей природе. (Исключением, как мы увидим в дальнейшем, явля¬
ется слово EXIT, которое позволяет немедленно прекратить исполнение слова.) Кроме того, по¬
скольку в любой подпрограмме некоторые слова можно переделать, дать им более короткие имена,
использование переходов становится не столь обременительным, как в Паскале.
Упражнения
1. Определите слово NEWABS (абсолютное значение), используя конструкцию IF...THEN. (Совет: воспользуйтесь слова¬
ми 0< и NEGATE).
2. Определите слово, подобное / (т.е. деление нацело), которое должно выдавать сообщение об ошибке при попытке деле¬
ния на нуль.
3. Определите слово TYPE<10, пользуясь TYPE, которое должно действовать как TYPE, но игнорировать попытку напе¬
чатать строку длиной более 10 символов.
4. Ваша программа выдает на экран меню, ожидая в качестве ответа одну из букв алфавита А, T или X. Определите сло¬
во 7WHICH-TASK (какая задача?), используя слово KEY, которое должно игнорировать любую введенную букву, кроме
ожидаемых, и исполнять одну из задач lTASK, 2TASK или 3TASK в зависимости от того, что введено: А, T или X.
5. Определите слово UPPER-KEY (перевод_в_верхний регистр), используя KEY, которое действует как KEY, но любую
букву нижнего регистра переводит в соответствующую букву верхнего регистра.
6. Определите слово NO-CNT-KEY (не_управляющая_клавиша), которое действовало бы как UPPER-KEY, но игнориро¬
вало бы также любую управляющую клавишу, т.е. код управляющей клавиши должен превращаться в код буквы верхнего
регистра.
7. Определите слово ALPHA-KEY (клавиша-буква), которое обладало бы свойствами слова NO-CNT-KEY, но, кроме то¬
го, игнорировало бы ввод цифр.
8. Определите слово, которое преобразует число двойной длины в одинарное число тогда, и только тоща, когда число
двойной длины меньше 65535.
Прекращение исполнения задания
Обычно исполнение слова Форта продолжается до тех пор, пока не встретится последнее слово в
его определении. Форт-программа исполняется до конца слова, которое вызвало его исполнение. В
95
некоторых случаях нужно прекратить исполнение слова или программы досрочно. Завершение ис¬
полнения слова возвращает управление слову, которое запустило программу, в противоположность
этому преждевременное прекращение программы возвращает управление терминалу.
Сначала мы познакомимся с тем, как можно выйти досрочно из исполнения слова с помощью
оператора EXIT. Пусть вы определили
: ADD + EXIT . ;
: SUM 3 4 ADD 5 6 ADD ." Суммы" ;
Если вы запустите слово SUM, а потом посмотрите, что находится в стеке, то увидите, что, хотя
обе пары чисел были введены и просуммированы, ни один из результатов не выведен на экран, по¬
тому что операция ADD была окончена раньше, чем встретилось слово * (напечатать). А сообщение
”Are the sums” (суммы) напечаталось. Дело в том, что, когда завершилось исполнение слова ADD,
программа SUM еще не завершилась и управление было передано слову SUM. Теперь попробуйте
тот же пример, заменив в нем EXIT на QUIT. Вы не увидите завершающего программу сообщения,
но если заглянете в стек, то увидите там только первую сумму. Слово QUIT не только прекратило
операцию ADD, оно завершило исполнение программы, передав управление клавиатуре. Если слово
QUIT сохраняет содержимое стека, то слово ABORT делает очистку стека.
Из данного примера не видно явно никакой пользы от новых слов EXIT, QUIT и ABORT. Зачем
же нужны слова, которые только прекращают действие программы или слова до их завершения?
Ответ состоит в том, что эти слова используются обычно в конструкциях IF...THEN, о чем мы
вскоре расскажем. Назовем лишь два самостоятельных употребления этих слов. Во-первых, ни
QUIT, ни ABORT не оставляют на экране сообщения ”ок”, если они стоят в конце программы.
Вспомним пример программы WT, в которой числа вводились после того, как вы печатали WT и
<BK>, и на экране получалось что-то вроде
223 WT ok
16 WT ok
59 WT ok
Можно сделать ввод более красивым, добавив в конце определения слова WT фразу SPACE
QUIT. Тогда после каждого WT <BK> на экране мы увидим
223 WT 16 WT 59 WT
Слово ABORT будет делать то же самое, но, кроме того, еще очищать стек. Использование слов
ABORT и QUIT может быть полезным для подавления сообщения ”ок”.
Помимо этого, самостоятельное применение указанных слов может оказаться полезным при от¬
ладке программы. Предположим, что вам нужно проверить, не приводят ли к ошибке слова, кото¬
рые вы ввели в конце какого-либо определения через двоеточие. Тогда можно сделать, чтобы эти
слова игнорировались бы с помощью слова EXIT (разумеется, то же можно сделать, помещая эти
слова в круглые скобки). Можно также применить QUIT, чтобы прервать исполнение программы в
каком-то месте и посмотреть содержиМое стека или переменных. Слово ABORT в этом применении
менее полезно, так как оно очищает стек, который нас, безусловно, интересует. Как уже было ска¬
зано, EXIT, QUIT и ABORT чаще всего используют в конструкции IF...THEN. Пусть нам нужно
обеспечить возможность останова исполнения программы в определенном месте. Используйте для
этого слово
• 9ABORT ” Нажмите S для прекращения программы"
KEY 83 =
IF ” ok" CR ABORT THEN ,
Если 7ABORT вставить в какое-либо место в программе, пользователь будет иметь возможность
остановить ее исполнение. А .”ok” CR введены в программу для того, чтобы напечатать сообщение
”ок” и сделать возврат каретки, чего не делает слово ABORT. Очевидно вы можете заменить его
словом QUIT, если хотите сохранить содержимое стека.
Слова ABORT и QUIT очень часто используются для обнаружения ошибок. Нам совершенно не¬
желательно разрешать деление на 0 (что может произойти из-за ошибки в вашей программе). Вот
такое слово, которое можно вставить в программу непосредственно перед оператором деления / для
того, чтобы выйти из вашей программы, если произойдет такая ошибка:
0/? DUP 0= IF .’’ Ошибка деления на 0" DROP QUIT THEN ,
Заметим, что DROP можно убрать, если использовать ?DUP, и тогда получим
0/? 9DUP 0= IF ” Ошибка деления на 0" QUIT THEN ,
Вам может потребоваться также досрочное завершение программы при достижении какого-либо
заранее определенного условия. Представим себе, что есть программа, которая принимает данные
96
от удаленного компьютера по телефонной линии, и нужно, чтобы она останавливалась, если будет
получен управляющий код Ctrl-C ( код ASCII равен 3). Можно после каждого входного символа
вставить проверку с помощью слова
: 7CTLC DUP 3 =
IF ." Прекращение по дистанционному запросу
ABORT THEN ;
Говоря вообще, слова ABORT и QUIT наиболее полезны для слежения за условиями, при кото¬
рых исполнение программы должно быть прекращено. Эти условия обычно связаны с некоторыми
типами ошибок, например делением на 0, но могут быть, конечно, и другого рода. Можно напом¬
нить, как мы говорили в гл. 1, что в Форте забота о проверке наличия ошибок целиком возлагается
на программиста. И мы показали, как она может быть сделана в простых случаях. Слово EXIT
имеет более тонкие применения, чем ABORT и QUIT. Оно прекращает исполнение слова и возвра¬
щает управление туда, откуда это слово было вызвано. Оно бесполезно вне конструкции IF...THEN,
поскольку всегда возможно прекратить исполнение более естественным путем, завершая определе¬
ние слова точкой с запятой. (Кстати, EXIT фактически является частью определения слова ;.)
Предположим, например, что у вас есть слово, которое позволяет оператору ввести число, а затем
запомнить сумму в переменной TOTAL. Вы хотите, чтобы ввод нуля игнорировался. Используя
EXIT, можно дать такое определение :
: GET# #IN 7DUP IF TOTAL +! ELSE EXIT THEN ;
но по своему действию
ELSE EXIT THEN ;
ничем не отличается от
THEN ;
Таким образом, EXIT следует применять ограниченно или не применять вовсе внутри одной
единственной конструкции IF...THEN. Вспомним наше определение слова (WT) :
: (WT) DUP 100 < IF TASK1 ELSE
DUP 200 < IF TASK2 ELSE
DUP 300 < IF TASK3
THEN THEN THEN DR0P ;
Теперь предположим, что слова TASK1, TASK2 и TASK3 прибавляют по единице к переменным
lCOUNT, 2COUNT и 3COUNT каждый раз, когда вес попадает в соответствующий диапазон значе¬
ний. Но вы не хотите, чтобы слово TASK исполнялось, если в каком-либо диапазоне общий счет
превысил 200. Иначе говоря, вы хотите прекратить исполнение, если количество случаев попадания
в какой-либо диапазон превышает 200. Представляем слово, которое решает эту задачу :
: (WT) DUP 100 < IF 1C0UNT @ 199 > IF EXIT THEN TASK1 ELSE
DUP 200 < IF 2C0UNT @ 199 > IF EXIT THEN TASK2 ELSE
DUP 300 < IF 3C0UNT @ 199 > IF EXIT THEN TASK3
THEN THEN THEN DR0P ;
Этот пример показывает, что если использование слова EXIT в единственной конструкции
IF...ELSE приводит к появлению лишних бесполезных слов, то во вложенных конструкциях
IF...ELSE слово EXIT становится существенно необходимым.
Прежде чем закончить этот раздел, укажем на менее употребительное слово из Форт-83 и других
версий — ABORT”. Это слово ищет флаг в стеке и исполняется, если его значение истина, т.е.
слово ABORT” содержит внутри себя конструкцию IF. Слово ABORT” выдает сообщение, которое
следует после ”, а затем исполняет операции присущие слову ABORT. Его назначение состоит в
том, чтобы обнаруживать ошибки и давать об этом сообщение. Вот, например, определение слова
0/? с использованием слова ABORT”:
: 0/? 7DUP 0= AB0RT" Ошибка деления на 0 " ;
Приведенные ниже упражнения покажут некоторые более полезные на практике применения
рассмотренных слов.
Упражнения
1. Определите слово ?END для оптимального выхода из программы, которая задавала бы следующие вопросы и выполня¬
ла бы соответствующие вашим ответам действия:
Do you want to quit? (Y/N)
Вы хотите закончить работу9
и если введен ответ У(да), то
4 М. Келли
97
Do you want to save the stack? (Y/N)
Вы хотите сохранить стек? (Да/Нет)
2. Если слово ?223 вводится непосредственно (не входит в определение через двоеточие), имеются ли какие-либо разли¬
чия в его действии при следующих трех способах его определения :
: ?223 223 = IF 1 COUNT +! ELSE QUIT THEN ;
: ?223 223 = IF 1 COUNT +! ELSE EXIT THEN ;
и
: ?223 223 = IF 1 COUNT + ! THEN ;
3. Как изменится действие слова из определения 2, если ?223 вызывается из другого определения через двоеточие?
4. Имеются ли различия, если в определении следующего слова используется DUP или ?DUP? :
: 0? DUP 0= IF AB0RT" Число равно нулю " THEN ;
5. Дайте новое определение слова ?0, используя слово ABORT.
6 Определите слово -IF-ABORT, которое прекращает исполнение программы, если два верхних числа в стеке равны
между собой.
7. Определите слово +RANGE-ABORT, которое прекращает работу программы и сообщает об ошибке, если сумма двух
чисел в стеке будет превышать максимальное число одинарной длины без знака.
8. Определите слово *+RANGE-ABORT, которое прекращает работу программы, если либо сумма, либо произведение
двух чисел в стеке превысят максимальное число одинарной длины без знака.
9. Определите слово STACK-TOO-BIG, которое выдавало бы сообщение об ошибке и прекращало исполнение программы,
если в стеке больше 15 чисел.
10. Дайте новое определение следующих слов, не используя слова EXIT:
а) : 1TASK 0= IF D0THAT ELSE EXIT THEN ;
б) : 2TASK 0= IF EXIT ELSE D0THAT THEN ;
в) : 3TASK 0= IF D0THAT ELSE EXIT THEN D00THER ;
г) : 4TASK 0= IF EXIT ELSЁ D0THAT THEN D00THER ;
Это послужит убедительным доказательством, что слово EXIT совершенно не нужно в невложенных конструкциях
IF...THEN.
Множественный выбор ветвления
До сих пор в этой главе мы рассматривали конструкции IF...THEN и IF...ELSE...THEN, которые
позволяют делать переход на исполнение одной из двух ветвей программы в зависимости от того,
равно или не равно нулю число в стеке. Однако вам может потребоваться программа, которая мо¬
жет разветвляться по альтернативным путям в зависимости от того, какое из нескольких чисел на¬
ходится в стеке. В гл. 6 мы показали один из вариантов решения этой задачи. Векторное исполне¬
ние программы — это одно из средств реализации ветвления по нескольким путям. Если компонен¬
ты вектора представляют собой адреса слов, то данное слово может быть исполнено, если взять его
адрес и применить к нему оператор EXECUTE. Таким образом, слово, на исполнение которого ухо¬
дит программа, зависит от одного из нескольких чисел, которое находится в стеке, когда выбирает¬
ся конкретный адрес. В гл. 6 мы хранили адреса слов в массиве и использовали слова FIND или \ в
зависимости от применяемой версии Форта. Давайте снова рассмотрим пример из гл. 6. Пусть слова
lTASK, 2TASK и 3TASK определены. Можно задать вектор CHOICE (выбор) следующим образом :
CREATE CHOICE 6 ALL0T
FIND 1TASK CHOICE !
FIND 2TASK CHOICE 2 + !
FIND 3TASK CHOICE 4 + '
Теперь если мы определим слово DOCHOICE (сделай_выбор)
: D0CH0ICE 1- 2* CHOICE + @ EXECUTE ;
то исполняемое ветвление будет зависеть от числа, которое находится в стеке, когда запускается
программа DOCHOICE.
Эта конструкция носит название выбор по целому. Если в стеке находится 1, то выполняется
lTASK, если 2- то 2TASK и т.д. Такая конструкция выбора по целому, основанная на векторном
исполнении, едва ли не самое простое, что может быть придумано в стандартном языке Форт. Раз¬
нообразным реализациям структуры выбора по целому был посвящен целый выпуск журнала
’’FORTH Dimensions” (1980- Т. 2, вып. 3). В некоторых коммерческих версиях Форта реализованы
сложные конструкции выбора по целому. Мы рассмотрим слова NCASE и ACASE из языка
98
MMSFORTH. Проще всего для понимания слова NCASE привести простой пример. Пусть вы хотите
выполнить lTASK, 2TASK или 3TASK в зависимости от того, какое из чисел: 20, 40 или 60 нахо¬
дится в стеке, а если в нем встречается какое-либо другое число, нужно, чтобы исполнялось слово
OTHER (другая). Вот как это можно сделать :
NCASE 20 40 60 ” 1TASK 2TASK 3TASK OTHERWISE OTHER CASEND
Оператор NCASE снимает число из стека и просматривает, какому из следующего за ним ряда
чисел оно равно. Если обнаруживается совпадение, то исполняется соответствующее слово из пере¬
численного списка, если совпадение не обнаружено, то исполняется слово или последовательность
слов, находящихся между OTHERWISE (иначе) и CASEND (конец_выбора). Слово OTHERWISE и
любые слова после него не являются обязательными, т.е. если в данном случае вы не хотите, чтобы
альтернативным путем было использование слова OTHER, то можете использовать программу
NCASE 20 40 60 ” 1TASK 2TASK 3TASK CASEND
А теперь рассмотрим более сложный, но и более полезный пример. Пусть у вас есть программа, в
которой используется слово KEY для ввода чисел. Вы хотите, чтобы она печатала все, что является
алфавитно-цифровыми знаками, при вводе Ctrl-C (код ASCII 3) очищала бы экран с помощью сло¬
ва CLS,npn вводе Ctrl-G (код ASCII 7) включала бы звуковой сигнал (с помощью слова BEEP) и
при Ctrl-P (код ASCII 16) вывод переключался бы на принтер (с помощью слова PRNT). Это может
делать следующая программа :
. GETKEY KEY DUP
NCASE 3 7 16 ” CLS BEEP PRNT
OTHERWISE DUP 32 <
IF DR0P
ELSE EMIT
THEN
CASEND ;
Надеемся, что вы поняли, как она работает.
Одно из самых полезных применений слова NCASE состоит в обслуживании запросов, связанных
с нажатием определенных клавиш при ответе на предложения из меню. Слово NCASE будет рабо¬
тать правильно только с числами, не превышающими по величине максимального значения одного
байта (от 0 до 255), и оно устроено так, что фактически игнорирует старший байт 16-разрядного
числа. Это значит, что результат будет один и тот же, если NCASE обнаружит число 0A или AB0A
(в шестнадцатеричном представлении), т.е. оно ищет совпадение с 0A в одном из следующих после
него чисел. Аналогичным образом числа, следующие за NCASE, могут быть больше, чем представ¬
ляются одним байтом, при этом старший байт числа будет игнорироваться. Таким образом,
NCASE 55A3 2221 AC55 " . . .
будет исполняться так же, как
NCASE АЗ 21 55 ” ...
Следовательно, поскольку вы уверены, что старший байт числа никогда не может повлиять на
выбор, определяемый словом NCASE, можно применять числа больше 255. Слово ACASE в
MMSFORTH близко по назначению слову NCASE. Но оно реагирует не на число, а на символы
ASCII. Приведем пример его синтаксиса:
ACASE NKT” 1TASK 2TASK 3TASK OTHERWISE OTHER CASEND
Слово ACASE просматривает стек, и если оно обнаруживает букву N, то исполняется lTASK, ес¬
ли букву К- то 2TASK, а при T уходит на 3TASK. Если нет совпадения ни с одной буквой, выпол¬
няется слово OTHER. Так же как и в NCASE, слово OTHERWISE, слово или слова, следующие за
ним до слова CASEND, не являются обязательными. Заметьте, что не должно быть пробела между
буквами или между буквами и кавычкой, если только вы не используете пробел для обозначения
одного из вариантов выбора. Применение слова ACASE сходно с применением и NCASE, поэтому
вместо разбора примеров предлагаем вам освоить его применение на упражнениях.
Упражнения
1. Создайте конструкцию выбора по целому, пользуясь векторным исполнением, которая выполняла бы арифметические
действия над вторым и третьим числами в стеке по следующим правилам. Если в стеке находится 1, то слово ARITH выпол¬
няет сложение +; если 2, то вычитание -; если 3 или 4, то соответственно операции умножения * или деления /. Измените
слово ARITt}, пользуясь конструкцией IF так, что если число в стеке не попадает в диапазон чисел от 1 до 4, то в стек по¬
мещается 0.
4*
99
2. Дайте другое определение слова ARITH, используя слово NCASE (включите выдачу нуля в стек, если используется не¬
правильное число).
3. Определите слово NEWARITH, используя ACASE, так чтобы оно ожидало нажатия клавиши. Если будет нажата кла¬
виша ”+”, числа нужно сложить; если нажата клавиша ”-”, то вычесть; если нажата клавиша ”*” или "/", то умножить или
разделить соответственно. Если нажата неверная клавиша, то в стек должен выдаваться нуль.
4. Переделайте определение NEWARITH в NEWARITH1, используя конструкцию IF...ELSE...THEN. После того как вы
это сделаете, вы оцените пользу слова ACASE.
5. Переделайте NEWARITH, как описано в упражнении 3, чтобы при нажатии неправильной клавиши печаталось сооб¬
щение ” Неверный ввод ” и исполнение прерывалось. Проделайте это, применяя слова ABORT и ABORT*.
6. Можете ли вы придумать ситуацию, когда предпочтительнее использовать не NCASE и ACASE, а векторное исполне¬
ние?
Выводы
В некотором смысле возможность ветвления делает программу более эффективной. Ветвление по¬
зволяет одной программой выполнять различные действия при различных условиях. Но в то же
время применение ветвления заставляет программиста проявлять находчивость. Применение ветв¬
ления может привести к исключительной сложности программы. Таким образом, ветвление требует
тщательного продумывания при составлении программы. Без ветвления исполнение программы
представляется последовательностью операций, выстроенных в линию. При каждом запуске про¬
граммы одно слово исполняется за другим в одной и той же последовательности. Действительно, ес¬
ли не пользоваться ветвлением и векторным исполнением, то, хотя при этом может получаться
длинная и практически неудобная для чтения программа с большой избыточностью, для нее не по¬
требуется определять какие-либо новые слова, кроме имен программ. Альтернативы, обеспечивае¬
мые с помощью ветвления, требуют определения новых слов и, таким образом, создают дополни¬
тельные сложности при разработке программы. Ветвление более, чем что-либо другое, требует тща¬
тельной проработки, т.е. программист должен предусмотреть все возможные варианты выбора. Мно¬
го времени и терпения может потребоваться, если, написав программу, программист обнаруживает
неучтенные варианты разветвления, так как при этом часто требуется переписать основные куски
программы. Хотя структура языка Форт способствует облегчению решения таких проблем, но чем
больше вариантов (ветвлений) нужно включить в программу, тем тщательнее она должна быть про¬
думана.
Необходимость такого планирования вносит противоречие в подходе к разработке программ меж¬
ду программированием сверху вниз, при котором обдумываются все детали, прежде чем перейти к
составлению текста программы, и программированием снизу вверх, при котором программист сразу
же начинает определять и проверять действие новых слов, разумно полагаясь, что его интуиция не
приведет к ошибке. Как мы увидим в гл. 13, хорошая методика программирования на Форте вклю¬
чает в себя оба подхода одновременно, привлекая к разработке программы интуицию, творчество и
испытывая чувство удовлетворения от проделанной работы.
Глава 8
ОРГАНИЗАЦИЯ ЦИКЛОВ
Возможно, одним из наиболее полезных свойств компьютера является его способность многократно и с очень большой ско¬
ростью повторять операции. Циклы составляют часть программы, в которой повторяются одни и те же действия. Почти в
каждой программе так или иначе применяются циклы. Как вы увидите в гл. 15, сам Форт работает как бы в бесконечном
цикле.
В предыдущих главах мы пользовались наиболее распространенной формой цикла типа DO-LOOP. Однако мы еще не ис¬
пользовали все его возможности. Кроме того, существуют и другие типы циклов с еще большими возможностями, например
циклы типа BEGIN...UNTIL, которые вызывают повторение программы только до тех пор, пока не будет удовлетворяться не¬
которое определенное условие. Сначала мы рассмотрим, как работают счетные циклы DO-LOOP и некоторые их примене¬
ния.
Циклы типа DO-LOOP
Вы уже знакомы с простейшей формой счетного цикла:
: 10L00PS 10 0 DO 5 . LOOP ;
Слово lOLOOPS при исполнении напечатает на экране число 5 10 раз (т.е. от 0 до 9), а когда будет
достигнут счет 10, циклическая программа прекратится. Между прочим, так же, как и конструкцию
IF...THEN, счетный цикл можно использовать только внутри определений через двоеточие. По-ви¬
димому, самый простой счетный цикл, имеющий практическое применение, это
. PAUSE 2790 0 DO LOOP ; (Пауза)
который приостанавливает программу, пока исполняется пустой цикл. Таким образом, слово
PAUSE приостанавливает программу на время 2790 циклов или на l/10 c на IBM PC в версии
MMSFORTH; слово PAUSE может быть вставлено в другой цикл для получения еще больших за¬
держек. (Если вы привыкли к языку Бейсик, то для вас будет неожиданностью, насколько быстрее
выполняются циклы в Форте.) Очень важным моментом является то, что циклы могут вставляться
в другие циклы. Если вы определите слова
1L00P 5 0 D0 .” Внутренний цикл” L00P ;
и
2L00P 5 0 D0 CR .’’ Внешний цикл” CR CR 1L00P L00P ;
тогда каждый раз, когда будет исполняться цикл в программе 2LOOP, она напечатает в начале
строки свое сообщение ’’Внешний цикл”, сделает два перевода строки, а затем напечатает сообще¬
ние из программы lLOOP 5 раз, пока будет исполняться эта программа; так будет повторяться 5
раз, следовательно, сообщение ’’Внутренний цикл” будет напечатано 25 раз. Циклы, находящиеся
внутри других циклов, называются вложенными; глубина вложенности может быть такой, какой
она практически потребуется. Ради интереса попробуем, пользуясь циклами, сравнить скорость ра¬
боты языка Форт по отношению к Бейсику. Цифры, приведенные здесь, относятся к IBM PC для
MMSFORTH и версии Бейсика фирмы Microsoft. Первая программа цикла на Бейсике
10 DEFINT I,C
20 FOR 1=1 T0 10000
30 NEXT I
выполняется 11.5 с (заметьте, что для корректности по отношению к Бейсику мы пользуемся целы¬
ми числами). Если мы напишем программу
: TEST 10000 0 D0 L00P ;
то она будет исполняться слишком быстро, поэтому для определения времени ее работы мы будем
вынуждены включить ее во вложенный цикл:
: TESTA 10 0 D0 TEST L00P ;
который исполняется в течение 3.6 с, поэтому слово TEST, которое повторяет пустой цикл 10000
раз, исполняется за 0.36 с. Таким образом, счетный цикл на Форте работает в 32 раза быстрее, чем
на Бейсике. Рассмотрим теперь некоторые арифметические операции. Если мы вставим
25 C=10
в первую программу, то ее исполнение удлинится до 17.5 с. Программа на Форте, эквивалентная ей
в первом приближении:
101
: TEST 10000 0 DO 10 DROP LOOP ;
исполняется за 0.71 с. Теперь в строке 25 на Бейсике поместим
25 010+10
тогда выполнение программы займет 23.1 с. Следовательно, исполнение 10000 сложений целых чи¬
сел занимает 23.1 - 17.5, или 5.6 с. На Форте для исполнения программы
: TEST 10000 0 DO 10 10 + DROP LOOP ;
потребуется 1.13 с, т.е. 1.13 - 0.71 - 0.42 с на 10000 операций сложения. Следовательно, Форт быст¬
рее Бейсика по операции сложения в 13 раз. Аналогичное испытание показывает, что для операции
умножения на Бейсике требуется 5.7 с, а на Форте - 0.7 с, т.е. в 8 раз меньше. Другими словами,
для операций с целыми числами Форт работает примерно в 8 - 30 раз быстрее, чем Бейсик. Еще бо¬
лее интересно сравнить операции над числами с плавающей запятой для процессора Intel 8087. При
замене строки 25 на
25 D=10E10
программа работает 21.4 с; если в строке 25 будет
25 D=10E10+10E10,
то программа исполнится уже за 28.2 с, т.е. время исполнения 10000 сложений чисел с плавающей
запятой составляет
28.2 - 21.4 = 6.8 с. Программа на Форте
. TEST 10000 0 D0 % 10E10 FDR0P L00P ;
потребует 0.79 с, а программа
. TEST 10000 0 D0 % 1E10 % 1E10 F+ FDR0P L00P ,
исполняется за 1.23 с, т.е. 10000 сложений чисел с плавающей запятой происходят за 1.23 - 0.79 =
0.44 с, в 15.5 раза быстрее, чем на Бейсике, и менее чем на 5% медленнее операции сложения це¬
лых чисел на Форте. Умножение на Бейсике занимает 16.8 с, на Форте - 0.46 с, т.е. в 16 раз быст¬
рее и фактически на 34% быстрее, чем умножение целых чисел на Форте (мы уже говорили в гл.
4, что некоторые операции с плавающей запятой на процессоре 8087 выполняются быстрее, чем с
целыми числами). Извлечение квадратного корня на Бейсике производится за 14.5 с, а на Форт - за
0.87 с, т.е. в 39 раз быстрее. На самом деле эти сравнения не очень корректны по отношению к
Форту, поскольку последний совместно с процессором 8087 имеет в 100 раз больший диапазон пред¬
ставления чисел и более чем в 4 раза лучшую точность по сравнению с числами одинарной точно¬
сти в языке Бейсик. Если на Бейсике производится умножение чисел двойной точности, то фактор
преимущества в скорости для Форта равен 48, и это, несмотря на больший диапазон и повышенную
точность. Умножение на современной быстродействующей универсальной ЭВМ типа CDC Cyber вы¬
полняется в 20 раз быстрее, чем на Форте с микропроцессором 8087. Однако затратив менее 100
долл., можно увеличить скорость ваших вычислений больше, чем при переходе на универсальную
ЭВМ стоимостью около миллиона долларов.
Возвращаясь из нашего экскурса, мы должны рассмотреть индекс счетного цикла. Индекс пред¬
ставляет собой текущее значение счетчика, начинающего счет с нижнего значения и при каждом
повторении цикла изменяющегося на заданную величину приращения. (Индекс помещается на вер¬
шину стека словом I, наименование которого происходит от Index, или, если вам больше нравится,
указывающего, что выполняется I-й цикл.) Таким образом, программа
: SH0W-INDEX 10 5 D0 I . L00P ;
напечатает на экране
5 6 7 8 9
Заметьте, что числа следуют только до 9. Так происходит потому, что индекс цикла инкременти¬
руется словом LOOP и, когда он достигает крайнего значения, в данном случае 10, происходит вы¬
ход из цикла без возврата к слову DO еще раз. Очевидно, что если повторение исполнения цикла
производится с помощью DO, то в стеке должны быть по крайней мере два числа. Верхнее число
служит начальным значением индекса цикла, с которым происходит вход в цикл. Второе число в
стеке - это предельное значение параметра цикла, по достижении которого с помощью инкременти¬
рования индекса происходит выход из цикла. Если мы определили слово как
: SH0W-INDEX 7 2 D0 I . L00P ;
то оно выдаст на экране
2 3 4 5 6
Обе последние программы повторяются по 5 раз, но дают разные результаты вследствие различных
начальных и конечных значений индекса цикла. Задание диапазона изменения индекса - это важ¬
ный момент, потому что значение индекса часто используется для вычислений внутри цикла. Име¬
102
ются некоторые отличия в способе завершения счетного цикла в стандартах Форт-79 и Форт-83.
Рассмотрим слово
. 1LOOPTEST 5 5 DO I . LOOP ;
В Форт-79 слово lLOOPTEST попросту напечатает число 5 и программа остановится, папротив, в
Форт-83 вы увидите
5 6 7 . . 32766 32767 -32768 -32767 ... -1 0 1 2 3 4
т.е. будут напечатаны 65535 чисел. Теперь рассмотрим пример другой программы:
: 2L00PTEST -32766 32766 DO I U. LOOP ;
которая в Форт-79 также напечатает только одно число 32766 и остановится, в то время как в
Форт-83 она выдаст
32766 32767 32768 32769
Согласитесь, что эти различия приводят в замешательство.
Что же происходит? Что касается Форт-79, то объяснение простое. Слово LOOP добавляет 1 к
индексу и после этого проверяет, не нужно ли выйти из цикла, если индекс достиг или превысил
величину предельного значения параметра цикла, рассматриваемого как число со знаком. Поэтому
обе тестовые программы в Форт-79 печатают только одно число - начальное значение индекса и
прекращают работу. Что касается Форт-83, то его реакция более хитроумная. Он принимает реше¬
ние выйти из цикла не на основании факта равенства или превышения индексом предельного зна¬
чения параметра цикла, а, скорее, на основании факта перехода от состояния, когда предел не был
достигнут, к состоянию, когда индекс достигает значение предела. В первом случае lLOOPTEST,
инкрементируя индекс, изменяет его с 5 на 6, поэтому необходимое изменение состояния не проис¬
ходит, следовательно, цикл должен продолжаться со значениями индекса 7, 8 и т.д. После достиже¬
ния индексом значения 32767 добавление единицы приводит к изменению знака числа, так как
число без знака 32768 представляет собой число со знаком -32768, но опять же изменение состоя¬
ния не происходит. Каждый раз при исполнении цикла индекс увеличивается на 1 до тех пор, пока
он не достигнет значения 4, а затем изменится на 5, тут и происходит выход из цикла.
Действие второй тестовой программы в Форт-83 можно проанализировать аналогичным образом.
Добавление единицы к 32766 приводит к превышению предела, но переход от состояния индекса
ниже предела к состоянию, когда он равен пределу или превышает его, не происходит, поэтому
цикл продолжается до тех пор, пока не произойдет переход от -32767 к -32766 или, если рассматри¬
вать числа без знака, от 32769 к 32770. Это как раз необходимый переход состояния, поэтому цикл
прекращается. Важно помнить, что если в Форт-83> начальное значение индекса равно или пре¬
вышает предельное значение, то цикл будет продолжаться до тех пор, пока индекс не прибли¬
зится к пределу снизу. Форт-83 работает именно таким образом, в особенности если используются
числа без знака; это обеспечивает воэможность обращения с помощью индексации циклов ко всем
64K ячейкам памяти.
Следует также помнить о том, что Форт-79 выходит из цикла, принимая во внимание значение
числа со знаком, поэтому программа
: 3L00PTEST 3 -3 DO I . I U. LOOP ;
выдает
-3 65533 -2 65534 -1 65535 0 0 1 1 2 2
Очевидно при первом инкрементировании величина индекса без знака превышает значение преде¬
ла, но слово LOOP следит за условием с учетом знака. Форт-83 даст такой же результат, но не из-
за того, что рассматривается число со знаком или без знака, а потому что происходит переход зна¬
чения индекса от величины ниже предела к величине, равной пределу. Эти тонкости прекращения
цикла могут быть особенно обескураживающими в случае, когда начальное значение индекса равно
пределу, в частности в языках Бейсик, Фортран и других. Например, программа на Бейсике
10 FOR I = 5 TO 5
20 PRINT I
30 NEXT I
не будет ничего делать, потому что индекс цикла равен пределу и тело цикла игнорируется (опера¬
торы PRINT I и NEXT I никогда не исполняются). С другой стороны, мы видели, что в программе
lLOOPTEST, которая также начинается со значеНия индекса, равного пределу, слова внутри цикла
исполняются, печатая 5, хотя зачастую это нежелательно. Хуже того, программа lLOOPTEST в
Форт-83 исполняется 65535 раз, пока не произойдет переход от 4 к 5. Очевидно, что вы этого вовсе
не хотели. Можно избежать неудобств с помощью конструкции IF...THEN. Если не исключена воз¬
можность, что в цикле типа DO...LOOP начальное значение индекса больше или равно пределу, то
вы можете защитить программу, например, таким образом:
103
... OVER OVER >
IF
DO
( тело цикла )
LOOP
THEN ...
Если начальное значение индекса больше предела, цикл не будет исполняться. Циклы в Форте
не так изящны, как в Бейсике, но, как вы уже видели, они работают существенно быстрее. Следует
иметь в виду еще одну деталь: слово I может встречаться только в определениях слов, которые
включают в себя циклы DO-LOOP. Поэтому не будут работать следующие определения:
: IN-LOOP I . ;
: TRY-LOOP 5 0 DO IN-LOOP LOOP ;
Слово IN-LOOP выдаст вам какую-нибудь бессмыслицу.
Во многих версиях Форта предусмотрено необязательное слово Г, которое должно работать следую¬
щим образом. Если мы переопределим слова предыдущего примера:
: IN-LOOP Г . ;
: TRY-LOOP 5 0 DO IN-LOOP LOOP ;
то слово IN-LOOP напечатает значение индекса цикла, т. e. что мы хотели бы увидеть в первом
примере. Таким образом, слово Г возвращает значение индекса цикла, если оно используется внут¬
ри слова, которое вызывается из цикла. В большинстве реализаций Форта слово Г исполняется
только в том случае, если оно используется в слове, вызываемом непосредственно из цикла, поэто¬
му нельзя написать программу
: 2IN-L00P Г . ;
: 1IN-LOOP 2INL00P ;
: TRY-LOOP 5 0 DO 1IN-L00P ;
И поэтому TRY-LOOP выдает что-нибудь несуразное. Причину этого вы поймете в следующей гла¬
ве. В большинстве версий слово Г вызывается непосредственно внутри цикла, где оно выдает пре¬
дельное значение индекса цикла. Так, программа
: SHOWLIMIT 5 0 DO I . Г . CR LOOP ;
напечатает
0 5
1 5
4 5
Для получения значений индексов вложенных циклов используются еще два слова. Если слово I
выдает значение индекса внутреннего цикла, то J возвращает значение индекса внешнего по отно¬
шению к нему цикла, а К - индекс следующего внешнего цикла. (Слово J входит в стандарты
Форт-79 и Форт-83, а слово К предусмотрено только в некоторых версиях.) В качестве примера
рассмотрим программу
: TEST 5 0 DO
5 0 DO
5 0 DO
I . J . К . CR
LOOP
LOOP
LOOP ;
Запустив слово TEST, вы получите на экране
0 0 0
1 0 0
2 0 0
4 0 0
0 1 0
1 1 0
0 0 1
104
1 0 1
4 4 4
Возможность пользоваться вложенными циклами и их индексами обеспечивает широкий диапазон
применений, одним из которых является обращение с двумерными массивами. Пусть у вас имеется
матрица MATRIX размерности 5x5 в версии MMSFORTH. Программа .MATRIX (печать__матрицы),
которую мы приводим, напечатает эту матрицу по строкам с аккуратно оформленными столбцами:
.MATRIX 5 0 DO 5 0
DO
I J MATRIX @ 6 .R
LOOP CR
LOOP ;
Внешний цикл обеспечивает переход от строки к строке, в то время как внутренний цикл переби¬
рает элементы матрицы в строке, извлекает их содержимое и печатает значения элементов матри¬
цы, производя выравнивание их вправо в поле шириной шесть позиций. С помощью циклов можно
сделать еще многое, но, прежде чем продвигаться дальше, рассмотрим еще один пример, а потом
перейдем к упражнениям. Циклы очень важны в программировании, но мы можем только слегка
затронуть рассмотрение их возможностей. Другие примеры мы приведем в следующих главах. Да¬
вайте попробуем написать программу, которая возводит в степень второе число в стеке, при этом
показатель степени указывается числом на вершине стека. Таким образом,
2 4 **
должно выдать число 16. Вот эта программа:
: ** ( n1 n2 — n1~n2 ) OVER SWAP 1- 0
DO
OVER *
LOOP SWAP DROP , 4
Рассмотрим возведение числа 2 в 4-ю степень. Если числа 2 и 4 находятся в стеке, то последова¬
тельность операций OVER SWAP 1- возвратит в стек 2 2 3. Затем 0 DO запустит цикл, который бу¬
дет исполняться 3 раза, а в стеке останется число 2. Затем после каждого исполнения цикла в стеке
будет наблюдаться следующее:
2 4
2 8
2 16
Предложение SWAP DROP производит уничтожение в стеке числа 2, которое остается после завер¬
шения цикла. Данный пример показывает три общих проблемы применения циклов, бо-первых, ча¬
сто необходимо переставить данные в стеке, прежде чем войти в цикл. Во-вторых, поскольку цикл
многократно использует число, обычно нужно применять на вершине стека операции DUP, OVER и
др. И, в-третьих, по завершении цикла в стеке может что-то оставаться, поэтому в конце програм¬
мы может потребоваться оператор DROP. Но не будем торопиться, посмотрим, что произойдет, если
мы захотим возвести число в первую степень? Программа ** выполнит один цикл и выдаст в ре¬
зультате квадрат числа, т.е. введете ли вы 1 или 2, получится один и тот же ответ. Это как раз
пример неудобства проверки индекса в Форте на предельное значение, о котором мы уже говорили
выше. Но выход есть. Сделаем определение ** таким:
: ** ( n1 n2 -- n1~n2 ) OVER SWAP 1- 0
OVER OVER <>
IF
DO
OVER *
LOOP
ELSE DROP DROP
THEN SWAP DROP ;
Предложение OVER OVER <> IF разрешает запустить цикл только в том случае, если показатель
степени больше 1. В противном случае значение показателя степени и его копия снимаются со сте¬
ка двумя словами DROP, заключенными между словами ELSE и THEN. Этот пример приведен для
того, чтобы показать как можно вообще избежать запуска цикла, если оба значения параметра цик¬
ла равны между собой. Еще более эффективной будет следующее определение слова ** :
: ** ( n1 n2 — n1~n2 ) DUP <>
IF
105
OVER SWAP 1- 0
DO
OVER *
LOOP SWAP DROP
THEN ;
Предоставляем вам самостоятельно разобраться, как работает это определение и почему оно лучше.
Но и это не все. Осталась еще одна проблема. Любое число, возведенное в нулевую степень, прини¬
мается равным 1. Это также должно быть учтено в программе ** . Поэтому окончательное опреде¬
ление ** будет таким:
: ** ( n1 n2 — n1~n2 ) ?DUP 0=
IF
DROP 1
ELSE DUP 1 =
IF
DROP
ELSE
OVER SWAP 1- 0
DO
OVER *
LOOP SWAP DROP
THEN
THEN ;
В гл. 16 вы познакомитесь с определением слова ** на языке ассемблера, которое работает значи¬
тельно быстрее. Следующие упражнения позволят вам еще больше узнать о циклах DO-LOOP.
Упражнения
1. Опишите слово ASCIITAB, предназначенное для того, чтобы напечатать коды ASCII и их алфавитно-цифровые эквива¬
ленты. Перед тем, как использовать это слово, в стеке должны находиться верхнее и нижнее значения кодов ASCII . Тогда
65 68 ASCIITAB
должно печатать
65 А
66 В
67 С
68 D
2. Модифицируйте программу ASCIITAB и назовите ее ASCIICHAR, так чтобы она печатала коды и соответствующие им
символы в следующей форме:
33 34 35 36 37 38 39 40 41 42
! ” U $ % & ’ - /
113 114 115 116 117 118 119 120 121 122
q г s t u v w x у z
no 10 кодов в строке, показывая все печатные символы с кодами от 32 до 122.
3. Опишите слово ZERDIAG, которое обнуляет диагональ матрицы, т.е. результатом ее работы должно быть что-то вроде
0 3 6 7 8
2 0 9 4 3
5 5 0 9 8
1 2 8 0 9
6 1 6 9 0
4. Опишите слово SUMVOL, которое должно вычислять сумму чисел в каждом столбце матрицы размерности 5x5 из чи¬
сел длиной в 1 байт, если задан адрес матрицы. Пять значений суммы должны быть выданы в стек.
5. Напишите слово ХА5, которое должно возводить число, находящееся на вершине стека, в 5-ю степень.
6. Полезно было бы иметь эквивалент слова ** для чисел двойной длины D**, которое выдает в результате число двойной
длины, если входное число имеет одинарную длину. Опишите слово D** .
7. Напишите слово DUMP1, которое должно, начиная с известного начального адреса в памяти, представлять на экране
следующие 160 байтов в шестнадцатеричной системе в виде таблицы из 10 строк по 16 чисел в каждой.
106
8. Измените слово DUMP, назвав его DUMP2, так чтобы в начале каждой строки слева бьш показан адрес первого байта в
данной строке. Используйте представление чисел с выравниванием вправо, так чтобы все числа бьши выровнены в столбцах.
9. Измените слово DUMP, назвав его DUMP3, таким образом, чтобы оно печатало 10 строк (всего 80 байт), в каждой из
которых сверху печатались бы числа, а под ними - соответствующие им символы ASCII. Так как символы с кодами меньше
32 нельзя напечатать, замените их точками.
Ю.Создайте переменную SPCS (от spaces - пробелы). Опишите слово для подсчета пробелов в текстовом массиве разме¬
ром 1024 байта, начинающемся с адреса, указанного числом в стеке; результат должен быть записан в SPCS .
11. Создайте массив ALPHA, число элементов которого равно числу букв алфавита. Напишите слово ALPHACOUNT
(число_букв), которое должно анализировать указанное количество алфавитно-цифровых байтов и записывать число встре¬
тившихся в массиве букв в элементах массива ALPHA . Тогда фраза
32201 500 ALPHACOUNT
будет просматривать массив из 500 байтов, начинающийся с адреса 322201, и считать число различных байтов, причем
число встретившихся литер "А" будет запоминаться в нулевом элементе массива, число литер ”В” - в первом и т.д. Жела¬
тельно превратить все литеры нижнего регистра в литеры верхнего регистра, чтобы не учитывать коды меньше 65 и больше
90.
12. Опишите слово ALPHAPLOT для представления в виде гистограммы частоты появления литер в массиве ALPHA, при
этом каждый столбик постройте из соответствующих букв.
13. Не заглядывая в ранее приведенные определения, опишите слово .S .
Еще о циклах типа DO-LOOP
В некоторых случаях нужно, чтобы шаг приращения индекса в цикле был равен не единице, а
большему числу. А иногда необходимо, чтобы цикл проходился в обратном направлении. В таких
случаях нужно использовать слово +LOOP . Испробуйте следующую программу:
: SKIPL00P 10 0 D0 I 2 +L00P ;
тогда вы увидите на экране
0 2 4 6 8
Индекс в цикле инкрементируется на 2, т.е. на то число, которое +LOOP находит в стеке. Теперь
определим слово
COUNTDOWN 0 10 I -2 +L00P , (обратный счет)
Использование слова COUNTDOWN приводит к выводу на экран чисел
10 8 6 4 2 0
Другими словами, индекс получает приращение -2, пробегая от 10 до 0. Обратите внимание, что
нуль также был напечатан. Если +LOOP декрементирует индекс (обратный цикл), то цикл закан¬
чивается только тогда, когда индекс оказывается меньше предела в Форт-79 или когда он совершает
переход от значения, равного пределу, к значению на единицу меньше предела в Форт-83, в то
время как при возрастающем индексе (прямой цикл) выход из цикла происходит, когда индекс ста¬
новится больше или равен пределу. Заметьте также, что различия в критериях для выхода из цикла
+LOOP в Форт-79 и Форт-83 такие же, как и для выхода из цикла LOOP . В последующих упраж¬
нениях мы рассмотрим применение циклов с +LOOP .
Бывают случаи, когда необходимо выйти из цикла раньше, чем индекс достигнет значение преде¬
ла. Чтобы сделать это, применяется слово LEAVE . Попробуйте этот практически бесполезный при¬
мер:
LEAVE-L00P 10 0 D0 I I 5=IF LEAVE THEN ."Loop” L00P ;
На Форт-79 исполнение LEAVE-LOOP (выйти_из__цикла) дает
0 Loop 1 Loop 2 Loop 3 Loop 4 Loop 5 Loop
Обратите внимание, что слово ’’Loop” (цикл) было напечатано после номера 5, хотя слово LEAVE
встретилось раньше и уже было исполнено. Так происходит потому, что в Форт-79 LEAVE просто
устанавливает значение предела равным значению индекса, исполняет все, что осталось в цикле, а
затем выходит из цикла в том месте, где LOOP проверяет величину индекса. На Форт-83 слово
LEAVE-LOOP при исполнении выдает на экране
0 Loop 1 Loop 2 Loop 3 Loop 4 Loop 5
Слово "Loop” после номера цикла 5 отсутствует. Это объясняется тем, что на Форт-83, когда
встречается слово LEAVE, оно, не обращая внимания на индекс и предел, делает скачок на слово,
которое следует сразу же после LOOP . Различия такого рода незначительные и достаточно тонкие,
но, если их не осознавать, они могут вызвать недоумение, когда с ними сталкиваешься. (Мелкое
замечание: I . I 5 - в программе LEAVE-LOOP можно записать иначе: I DUP . 5 «, но предыдущая
107
запись предпочтительнее, поскольку она более понятна, а по скорости работы обе конструкции оди¬
наковы.)
Приведем определение более полезного слова GET$ . Это слово принимает счетную строку литер с
клавиатуры, помещая длину строкй в PAD, за которой следуют литеры строки:
: GET$ 0 PAD С!
256 0
DO KEY DUP 13 =
IF
DROP LEAVE
ELSE
DUP EMIT PAD C@ 1+ PAD С! I PAD + 1+ С!
THEN
LOOP ; '
По адресу PAD вначале записывается нуль, затем запускается цикл, ожидающий ввод с клавиатуры
с помощью KEY до 256 символов. Если KEY вводит код 13 (код возврата каретки, одновременно за¬
пуска программы), то слово LEAVE выводит программу из цикла или подготавливает к выходу в
зависимости от того, в какой версии, Форт-79 или Форт-83, применяется программа. В противном
случае следующие после ELSE слова посылают на экран принятый байт и запоминают его в соот¬
ветствующем месте после адреса PAD, а также увеличивают значение счетчика введенных симво¬
лов на 1. Можно проверить работу этого слова, если ввести PAD COUNT TYPE . (Заметим, что
лучшее определение слова GET$ будет дано в гл. 9.) Другие применения циклов с +LOOP и LEAVE
лучше всего рассмотреть в упражнениях.
Упражнения
1. Опишите слово .ARR для печати содержимого одномерного массива чисел одинарной длины, начальный адрес которого
и его длина находятся в стеке.
2. Опишите аналогичное слово .SQARR для печати содержимого квадратной матрицы чисел одинарной длины (но не бай¬
тов, как в предыдущем примере) в виде строк и столбцов, если адрес и число строк (столбцов) находятся в стеке.
3. Повторите упражнения 1 и 2, но для чисел двойной длины.
4. Преобразование температуры по Фаренгейту в значение, выраженное в градусах Цельсия, производится по формуле
С = 5(F - 32)/9
Напишите слово, которое создает таблицу из двух колонок величин, выраженных в градусах Фаренгейта, преобразованных в
величины, выраженные в градусах Цельсия через 10 градусой от 0 до 200 градусов Фаренгейта. Меньшие значения должны
стоять в таблице сверху. Переделайте это слово для печати в четыре колонки.
5.Переделайте определение слова из упражнения 4, чтобы большие значения находились сверху таблицы.
6. Напишите слово FINDCHAR (найти_символ), которое будет просматривать последовательность из 1024 литер и выда¬
вать адрес, по которому впервые встречается литера, код ASCII которой помещается на вершине стека. Начальный адрес
должен быть вторым в стеке.
7. Опишите слово $-, которое будет просматривать две счетные символьные строки, адреса которых находятся в стеке, и
возвращать значение истина, если обе строки идентичны, и 'ложь в противном случае. Сначала должны быть проверены
длины строк, и, если они равны, строки нужно сравнивать посимвольно, применяя цикл, который должен прерываться, ес¬
ли обнаружено несовпадение, чтобы сократить время.
8. Опишите слово $FIND для поиска строки в символьном массиве из 1024 элементов, которое выдает адрес, если задан¬
ная строка обнаружена и 0 в противном случае. Адрес начала блока должен быть вторым в стеке, адрес заданной счетной
строки поиска должен быть на вершине стека. Используйте цикл для поэлементного сравнения строки с содержимым мас¬
сива, и, конечно, если какой-либо символ не совпадает, нужно перейти к следующей строке, начинающейся на следующем
символе. Считайте, что первый байт в массиве содержит длину строки.
Стек возвратов
Мы уже раньше упоминали о том, что в Форте имеются фактически два стека. Одним из них мы
пользовались. Это стек данных, иначе, стек параметров или стек пользователя. Стек возвратов
используется главным образом самой Форт-системой. Его основное назначение состоит в слежении
за адресом возврата при исполнении слова. Кроме того, в большинстве реализаций языка в нем
также сохраняются значения пределов для циклов и текущего значения индекса (хотя стандарты
этого не требуют). Его также можно использовать для временного хранения чисел. При этом важно
108
помнить, что, если стеком возврата пользуется программист, его нужно восстанавливать в ис¬
ходное состояние до окончания определения слова или до выхода из цикла.
Для операций в стеке возвратов предусмотрены три стандартных слова: >R (в_стек), R> (из_сте-
ка) и R@ (выдать_содержимое). Слово >R берет слово из стека параметров и помещает его в стек
возвратов, слово R> забирает число из стека возвратов, и помещает его в сек параметров, а слово
R@ выдает копию содержимого стека возвратов в стек пользователя, не изменяя содержимое стека
возвратов. Приведенные слова используются в стеке возвратов в основном для временного хранения
чисел, которые во время исполнения слова могут потребоваться несколько раз. Это может сильно
упростить некоторые манипуляции в стеке и зачастую позволяет обойтись без переменных для хра¬
нения чисел. Приведем пример. Пусть вы хотите извлечь однобайтовые элементы 5, 7, 11 и 12 из
массива с именем DATA в стек пользователя. Вот как это можно определить:
: @ELEMENTS ’ DATA 5 + C@ ' DATA 7 + C@
* DATA 11 + C@ ’ DATA 12 + C@ ;
Однако многократное повторение ’DATA снижает быстродействие. Быстрее будет работать следую¬
щая программа:
: @ELEMENTS ’ DATA >R R@ 5 + C@ R@ 7 + C@
R@ 11 + C@ R> 12 + C@ ;
Обратите внимание, что в последний раз адрес массива DATA берется из стека возвратов с по¬
мощью R>, поэтому в нем не остается ничего лишнего, когда мы завершаем исполнение слова по ; .
Использование стека возвратов в счетных циклах также очевидно. При исполнении операторов, на¬
ходящихся внутри цикла, верхний предел цикла лежит вторым в стеке возвратов, а текущее значе¬
ние параметра цикла, т.е. индекс, — на вершине стека возвратов.
Попробуйте исполнить следующее слово:
: TRY-IT 5 0 DO R@ . R> R@ . >R SPACE LOOP ;
Вы увидите
0 5 1 5 2 5 3 5 4 5
Выражение R@ . печатает число с вершины стека возвратов, т.е. индекс, поэтому R@ . можно ис¬
пользовать вместо I . . Затем R> снимает число с вершины стека возвратов, а R@ . извлекает чис¬
ло, которое было вторым в стеке возвратов, и печатает его. Это число 5, т.е. верхний предел цикла:
Затем >R снова помещает индекс в стек возврата. Попробуйте теперь действие следующих слов:
I-BAD R@ ; (неверное_1)
И
: TRY-THIS 5 0 DO I-BAD . LOOP ,
Если вы исполните слово TRY-THIS, то увидите какую-то чушь. Но мы только что сказали, что
R@ — то же самое, что I, где же ошибка? Вспомните, мы говорили раньше, что стек возвратов ис¬
пользуется для экономии внешних ячеек памяти, когда слово вызывается из определения через дво¬
еточие. При обращении к I-BAD адрес возврата помещается в стек возвратов, его вы и видите. Дру¬
гими словами, когда вызывается I-BAD, адрес возврата помещается на вершине стека возвратов —
выше, чем текущее значение параметра цикла (индекса). Поэтому правильное определение I долж¬
но быть таким:
: I R> R@ SWAP >R ;
Слово I должно сначала снять число с вершины стека возвратов, потом извлечь содержимое индек¬
са, а затем заменить число на вершине стека возвратов.
Теперь мы сможем дать определение Г. Просмотрите снова слова TRY-LOOP и IN-LOOP не¬
сколькими страницами назад и вспомните, что Г вызывается внутри слова, включенного в цикл,
чтобы извлечь индекс. IN-LOOP помещает в стек возвратов число (адрес) сверху индекса, а Г еще
выше помещает другой адрес. Поэтому, чтобы извлечь индекс, нужно вначале снять из стека воз¬
вратов два числа. Правильное определение I’ будет таким:
I’ R> R> R@ SWAP >R SWAP >R ;
Вам должно быть понятно, как работает это определение по аналогии с I. Понимаете ли вы, поче¬
му, если использовать его внутри цикла, оно возвращает значение верхнего предела параметра
цикла?
Вот так (в упрощенном виде) работает счетный цикл в Форт-79. Слово DO переносит первые два
числа из стека параметров в стек возвратов. Когда встречается слово LOOP, оно сначала прибавля¬
ет 1 к числу, находящемуся на вершине стека возвратов, а затем сравнивает результат со вторым
числом в стеке возвратов, т.е. с пределом на равенство либо превышение. Если это произошло, то
управление передается слову, которое стоит после LOOP. Если же индекс не равен пределу и не
превосходит его, то управление возвращается слову, которое стоит после DO. Цикл типа +LOOP
109
работает в Форт-79 аналогично, но вместо добавления 1 к числу, находящемуся на вершине стека
возвратов, добавляется число, находящееся на вершине стека параметров. Оперируя стеком возвра¬
тов, мы можем определить другие полезные слова для работы с циклами, но лучше всего рассмот¬
реть их в упражнениях.
Упражнения
1. Опишите слово 4PICK, пользуясь стеком возвратов для временного хранения чисел, которое должно печатать и удалять
из стека параметров четвертое сверху число. Почему в определении нельзя использовать цикл DO-LOOP?
2. Используя стек возвратов, дайте возможное (но не эффективное) определение DUP с именем DUP1.
3. Что находится в стеке возвратов на первых четырех местах сверху вложенного цикла? Исходя из этого, дайте определе¬
ние слова J (под именем J1). Не забудьте, что само слово J выдает число в стек.
4. Напишите определение слова К.
5. Опишите слово J\ которое выполняло бы функции слова I*, т. e. слово, в котором используется J', помещенное во вло¬
женный цикл глубины 2, должно давать значение индекса внешнего цикла.
6. Дайте определение слова LEAVE, назвав его NEWLEAVE, которое будет правильно работать на Форт-79 (вы должны
установить значение индекса цикла равным значению его предела).
7. Как можно изменить предел цикла из тела цикла?
8. Вы хотнте, чтобы в цикле, завершающемся по LOOP, по достижении индексом значения 10 он изменялся на 15. Какие
слова нужно включить в дело цикла, чтобы сделать это?
9. Опишите слово, которое заставляет индекс цикла изменяться на n, где n — число, которое находится в стеке в момент
исполнения слова.
10. Что будет делать следующее слово:
: DUMB 5 1 DO R> DUP 1- >R LOOP ,
(Оно очень коварно; будьте готовы к перезагрузке вашего компьютера.)
Для тех, кто знаком с дифференциальным исчислением
Упражнения
1. Поэкспериментируйте с разными значениями N в приведенном примере. Программа не работает, если N меньше 200.
Почему? Если вы пользуетесь версией Форт, позволяющей работать с плавающей запятой, перепишите программу для чисел
с плавающей запятой; она будет работать с меньшими начальными значениями.
2. Напишите программу, которая определяет, через какое время популяция в приведенном примере удваивается. Исполь¬
зуйте LEAVE.
3. Прирост популяции при ограничении на ресурсы описывается уравнением
dN К - N
= rN ,
dt К
где г — максимально возможная скорость роста и К — максимальная популяция, которая может сохраняться при ограничен¬
ных ресурсах. Напишите программу, моделирующую процесс роста популяции при начальном значении 1000, r, равном
1/200, и К, равном 3000.
4. А эта задача для честолюбивых. Просмотрите программы для построения гистограмм из гл. 5 и напишите программу
для представления роста популяции к упражнению 3.
Циклы с неопределенным числом повторений
Циклы типа DO-LOOP имеют ограниченные пределы, задаваемые из стека. Однако часто нужно иметь циклическую про¬
грамму, выход из которой зависит от выполнения некоторого условия, которое ею проверяется. Слово LEAVE позволяет это
делать, однако существует более удобная форма цикла — цикл с неопределенным числом повторений, который включает
слова BEGIN...UNTIL или BEGIN...WHILE...REPEAT. Как и цикл DO-LOOP, неопределенные циклы могут быть исполнены
только в определениях через двоеточие.
Рассмотрим вначале цикл BEGIN...UNTIL. Слово BEGIN отмечает начало цикла. Если в стеке находится 0 (флаг ложь),
то, когда его встречает слово UNTIL, исполнение повторяется со слова, которое стоит после BEGIN. Иначе говоря, цикл
BEGIN...UNTIL повторяется до тех пор, пока в стеке перед словом UNTIL не встретится число, не равное нулю. Для приме¬
ра определим слово
110
: TIL-10 BEGIN DUP . 1+ DUP 9 > UNTIL DROP ;
Исполняя 5 TIL-lO, мы увидим
5 6 7 8 9
To есть программа печатала число, находящееся на вершине стека, увеличивала его на единицу до тех пор, пока оно не ста¬
ло равным 10, и тогда она вышла из цикла.
Приведем более полезный пример. Предположим, что вы сделали в 1989 г. вклад 500 долл. из расчета 10% годовых. Ког¬
да на вашем счету будет 1000 долл. или более и сколько будет каждый год? Можно рассчитать это с помощью программы
VARIABLE T0TAL 500 TOTAL !
: ?YEAR
BEGIN
TOTAL @ 10 / TOTAL +! 1+
TOTAL @ 1000 >
UNTIL
TOTAL @ . . 500 TOTAL ! ;
Обратите внимание, что, как и в случае цикла DO-LOOP, слова, находящиеся между BEGIN и UNTIL, будут исполнены
хотя бы один раз, потому что проверка условия производится последним словом в цикле.
Более гибкой является другая конструкция неопределенного цикла BEGIN...WHILE...REPEAT. Она подобна
BEGIN...UNTIL, за исключением того, что REPEAT будет повторять цикл до тех пор, пока в стеке слово WHILE встречает
ненулевое значение. Если флаг, предшествующий WHILE, имеет значение истина, то цикл исполняется до слова REPEAT,
а затем вновь возвращается к слову BEGIN. Если флаг имеет значение ложь, исполнение переходит на слово, которое следу¬
ет за REPEAT, покидая цикл. На рис .8.1 схематически показана эта идея.
Рис. 8.1
111
Выполнение,
если значение
флага истинно
Выполнение,
если значение
флага ложно
BEGIN
Слово 1
Слово 2
Флаг
WHILE
Слово 3
Слово 4
REPEAT
Рассмотрим другое определение слова TIL-10 из предыдущего примера:
: TIL-10 BEGIN DUP 10 < WHILE DUP . 1+ REPEAT ;
Как и раньше, при исполнении 5 TIL-10 получим 5 6 7 8 9. Но разница есть. Если в первом примере вместо 5 мы введем
15, то будет выведено только число 15, потому что цикл исполнится по крайней мере один раз. Если ввести 15 перед BEGIN
во втором примере, то WHILE передаст управление после слова REPEAT и число 15 выведено не будет. Иначе говоря, если
конструкция BEGIN...UNTIL требует исполнения находящихся между ними слов по крайней мере один раз, то конструкция
BEGIN...WHILE...REPEAT позволяет пропустить все без исполнения, если это необходимо. Вот еще одинпример. Это слово,
которое разрешает ввод только цифры:
: GET# CR .” Введите цифру ”
BEGIN
KEY DUP DUP 48 < SWAP 57 > 0R
WHILE
." Неверно, должна бытьЦИФРА "
REPEAT ;
Фрагмент, предшествующий WHILE, будет исполняться всегда, по крайней мере, один раз, а слово после WHILE никогда не
будет исполняться при выходе из цикла.
В сущности BEGIN...WHILE...REPEAT можно использовать вместо конструкции IF...THEN (хотя и очень неэффективно).
Немного подумав, вы увидите, что
BEGIN WHILE ... 0 REPEAT
будет действовать так же, как
IF ... THEN
Почти точно так же
BEGIN ... 0= WHILE REPEAT
будет давать тот же результат, что и
BEGIN ... UNTIL
разумеется, более медленно. Вероятно, чаще всего используют конструкцию BEGIN...WHILE...REPEAT для того, чтобы иск¬
лючить даже однократное исполнение находящихся внутри слов, пока не будет удовлетворено некоторое условие, как в при¬
мере TIL-10. Другое общепринятое применение бесконечного цикла — для того, чтобы какой-либо фрагмент не исполнялся,
когда начнет удовлетворяться некоторое условие, как в примере слова GET#. Вот более сложный пример программы, кото¬
рую вы можете использовать как учебное пособие для освоения кодов ASCII. Она позволяет проверить знание до 20 симво¬
лов:
: ASK CR ." Какой код имеет литера ” ;
: T00BIG CR ." Число очень велико. Попробуйте еще.” ;
: T00SMALL CR ." Число очень мало. Попробуйте еще. ” ;
: GUESS 20 0
DO
CR .” Нажмите любую клавишу буквы или цифры ”
KEY DUP EMIT >R
BEGIN
ASK R@ EMIT #IN DUP R@ <>
WHILE
R@ <
IF T00SMALL ELSE T00BIG THEN
REPEAT
CR .” Вы УГАДАЛИ!!! " R> DROP
LOOP ;
После небольшого анализа вы поймете, как работает эта "угадайка”. Этот пример незатейливой обучающей программы
GUESS показывает, что, применяя цикл BEGIN...WHILE...REPEAT, можно написать программу значительно проще, чем ис¬
пользуя только цикл BEGIN...UNTIL.
Упражнения
1. Определите слово .S под именем NEW.S, используя цикл BEGIN...
2. Определите слово ST-SUM для суммирования содержимого стека, используя цикл BEGIN... Дайте другое определение
с именем ST-SUM1 с помощью цикла DO-LOOP.
3. Измените программу ?YEAR, назвав ее ?YEAR1, чтобы она печатала год, увеличение вклада за год и сумму на счете в
конце каждого года.
112
4. Пусть популяция мышей увеличивается на 0.05, или 1/200, за каждый день. Напишите слово, позволяющее рассчи¬
тать, 4ejtt3 сколько дней популяция удвоится. Используйте при этом цикл BEGIN... и LEAVE.
5. Дайте новое определение слова GET$, приведенного выше, с использованием цикла BEGIN... и без ограничения длины
строки 256 символами.
6. В MMSFORTH предусмотрено слово Y/N, которое печатает (Y/N)? и останавливается в ожидании нажатия клавиши.
Если нажата клавиша "Y”, в стек кладется 0, если ”N” — то 1. Если нажимают любую другую клавишу, ожидание продол¬
жается. Определите слово Y/N с именем Y/N1.
Выводы
Циклы представляют собой управляющие структуры, как и рассмотренные в гл. 7 конструкции
IF...THEN. И так же, как конструкция IF...THEN относится к фундаментальным средствам управ¬
ления программным потоком, циклы представляют главное средство повторного выполнения про¬
грамм, для чего в основном и используются компьютеры. Самой важной причиной могущества ком¬
пьютеров является их способность манипулировать с битами информации с очень большой скоро¬
стью. Если бы не существовало циклов, скорость их работы не приносила бы практической пользы.
Иначе говоря, циклы важны для составления практически полезных программ. По существу, язык
Форт (либо другой язык с диалоговым режимом, программа редактора и многое другое) представля¬
ет собой бесконечный цикл, который периодически посматривает на клавиатуру и исполняет то, что
вы приказываете.
Известны циклы двух типов: такие, как DO-LOOP с определенными извне пределами, и неопре¬
деленные циклы типа BEGIN...UNTIL, в которых пределы устанавливаются внутри цикла. Язык
Форт обращается с циклами по крайней мере не хуже других языков программирования. Заметьте,
между прочим, что неопределенные циклы могут быть также, что называется, ’’бесконечными”.
Например, следующий цикл никогда не закончится:
: INFINITY BEGIN KEY EMIT 0 UNTIL ;
Слово UNTIL никогда не встретит значение флага истина, поэтому цикл не прекратится никогда.
Следовательно, циклы могут быть опасны, если вы допустите подобную оплошность.
Фактически известен еще один тип цикла, который позволяет организовать так называемую ре¬
курсию. Рекурсия попросту состоит в том, что слово имеет возможность вызывать само себя, что в
некоторых случаях может быть очень полезным. Однако для ее правильного использования необхо¬
димо хорошо понимать внутреннее устройство самого языка Форт, и мы вернемся к этому вопросу в
гл. 15.
Глава 9
СИМВОЛЬНЫЕ СТРОКИ
В предыдущих главах мы употребляли термин ”строка'\ предполагая, что его смысл совершенно очевиден. Строка (сим¬
вольная строка) — это последовательность алфавитно-цифровых символов, которая запоминается в байтах памяти в
виде последовательности кодов ASCII. Таким образом, строка ’’STRING” будет запоминаться в памяти в виде последова¬
тельности десятичных значений байтов:
83 84 82 73 78 71
S T R I N G
Любой язык программирования в какой-то степени может обращаться со строками. Ввод и вывод (даже чисел) произво¬
дится с использованием строк, листинг и исходный код программы существуют в виде строк, так же как и сообщения опера¬
тору. Даже мнемоника языка ассемблера представляется символьными строками. Когда в гл. 5 вы пользовались форматным
выводом (<# # #>), вы также преобразовывали числа в строки.
Но помимо того, что строки используются для осуществления ввода-вывода, вывода меню в программах и приглашений-
подсказок, хороший язык программирования позволяет производить со строками некоторые операции в памяти как со специ¬
альным типом данных. Это дает вам возмбжность выполнять такие действия, как ввод строк с клавиатуры, посылку строк на
экран и принтер, хранение их в строковых переменных, добавление одной строки к концу другой, извлечение части строки
из более длинной строки, сравнение строк и т.д. Программы для управления базами данных, программы для обработки тек¬
стов, редактор и многие другие манипулируют с большими объемами строковой информации. Примером языка, очень удоб¬
ного для работы со строками, может служить Бейсик, неудобен для обработки строк Фортран. Другие языки программирова¬
ния располагаются где-то между ними. B соответствии со стандартами Форт — не очень хороший язык для работы со стро¬
ками, но, к счастью, он настолько гибкий, что не составляет большого труда добавить в него мощные слова для строковых
операций. Мы покажем вам, как определить некоторые слова такого рода.
Различные версии языка Форт, поставляемые фирмами, предоставляют пользователям широкие возможности обработки
строк, некоторые из них мы покажем вам на примере версии MMSFORTH. Вы увидите, что можно определить почти любое
слово, которое вам может потребоваться для работы со строками.
Для описания строки требуются две компоненты: содержимое (текст) строки и ее длина. В английском языке длина стро¬
ки задается с помощью разделителей, например пробелов, кавычек, точек и запятых. Такой же способ может быть принят и
для компьютера. Например, заключительная кавычка в выражении
.” This is а string ” (Это строка)
не является словом языка Форт, это — разделитель. Слово .” знает, что строка заканчивается там, где в тексте встретится
знак кавычки. Разделителем является также закрывающая скобка в комментариях. В качестве разделителя в Форте можно
использовать любое значение байта, однако чаще всего применяется пробел, которым при вводе должны быть разделены сло¬
ва (или числа). Другой способ задания строки состоит в указании ее длины числом. Давайте обсудим этот способ более
подробно.
Строки счетной длины, их ввод с клавиатуры
Строка, длина которой указана числом, называется в Форте счетной (реже говорят нумерован¬
ной строкой). Число символов, или счет, обычно указывается в байте-счетчике, который помеща¬
ется перед текстом строки (тем самым длина строки ограничивается 256 символами). Строка пред¬
ыдущего примера будет запомнена в памяти в виде счетной строки так:
06 83 84 82 73 78 71
S T R I N G
Но как же ввести строку в компьютер? Кроме слова KEY, с которым мы познакомились в гл. 5, для
ввода строк предусмотрено еще несколько слов: EXPECT, WORD и QUERY.
Рассмотрим сначала слово EXPECT. (Коротко о нем говорилось в гл. 5.) Давайте создадим слово
$IN (ввести_строку), которое будет печатать знак вопроса и ждать вашего ввода строки с клавиату¬
ры. Когда вы нажмете клавишу ввода <BK>, строка будет запоминаться в виде счетной строки, а
адрес ее начала (адрес байта-счетчика) будет помещен в стек. Сначала установим, имеется ли в
словаре вашей версии Форта слово BLANK (пробел). Это слово не является обязательным в
стандартах, но включено почти во все версии Форта. Если же это слово отсутствует, то его можно
легко определить:
: BLANK ( адр n —) 32 FILL ;
114
Напомним, что FILL заполняет n байтов, начиная с адреса адр, кодом ASCII, находящимся на вер¬
шине стека, в данном случае кодом пробела 32. Таким образом, слово BLANK заполняет n байтов
пробелами, начиная с указанного адреса. Нам потребуется также слово 2DUP; если у вас нет его в
системе, определите его так:
. 2DUP ( n1 n2 — n1 n2 n1 n2) OVER OVER ;
По мере продвижения нашего определения слова $IN мы будем нуждаться для проверки в словах
COUNT и TYPE (что мы уже видели в гл. 5). Напомним, что слово TYPE предполагает в стеке ад¬
рес строки (не счетной) и число символов в строке и после этого выводит указанное число символов
из строки. Слово COUNT просматривает содержимое указанного адреса и возвращает в стек нахо¬
дящийся там байт и, кроме того, адрес первого байта строки, подготавливая стек к использованию
слова TYPE. Слово COUNT можно определить следующим образом:
: COUNT ( $адр — $адр+1 n) DUP 1+ SWAP C@ ;
Поэтому если $адр — адрес байта-счетчика строки, то при исполнении
$адр COUNT TYPE
на экран будет выведена строка. Обратите внимание, что мы обозначили адрес байта-счетчика как
$адр (иногда применяется обозначение $). Очевидно, что это такой же адрес, как любой другой, но,
чтобы подчеркнуть, что по этому адресу хранится строка, мы используем символ $ .
Будем определять $IN в несколько этапов, забывая с помощью FORGET предыдущие определе¬
ния. Сначала определим
$IN PAD 1+ 255 BLANK ;
и испытаем это слово, вводя
$IN PAD 1+ 255 TYPE
в результате вы увидите на экране 255 пробелов — это все, что содержит строка на данный момент.
Обратите внимание, что мы собираемся создать строку со смещением на один байт относительно ад¬
реса буфера PAD, чтобы зарезервировать место для байта-счетчика. Теперь забудем старое опреде¬
ление: FORGET $IN и сделаем новое:
$IN PAD 1+ 255 2DUP BLANK .’’ ? " EXPECT ;
Испытаем новую версию, введя слово $IN. После этого на экране мы увидим знак вопроса, после
которого не будет слова ”ок”. Форт ожидает, что вы будете вводить строку, и, когда вы нажмете
клавишу ввода <BK>, эта строка будет записана в память. Попробуйте ввести
$IN ? THIS IS А TEST <BK> (ЭТО ТЕСТ)
(Знак вопроса ”?” был напечатан компьютером: вы ввели ”THIS IS А TEST” (”Это тест”.) Теперь
введем
PAD 1+ 14 TYPE
Тогда вы увидите на экране
THIS IS А TEST
Вы приказали компьютеру, чтобы он вывел 14 символов, начиная с адреса PAD+1, на экран, и
убедились, что это были символы, введенные словом EXPECT. Слово EXPECT ищет адрес и ука¬
занное в стеке число симв.олов. Мы указали адрес и PAD плюс 1, так как именно этот адрес и чис¬
ло 255 были указаны в определении слова $IN и остались в стеке благодаря слову 2DUP. Слово
EXPECT ожидает, когда будут введены символы строки. Если ввод прекращается с помощью кла¬
виши <BK>, то словом EXPECT будут запомнены только те символы, которые были введены до на¬
жатия клавиши <BK>. Если при вводе будет превышено указанное число символов (в данном слу¬
чае 255), то слово EXPECT будет действовать так же, как если бы вы нажали клавишу ввода <BK>.
Но наше слово еще не закончено, так как не было запомнено число введенных символов. Снова за¬
будем предыдущее определение FORGET $IN и дадим, наконец, полное определение:
. $IN ( — $адр) PAD 1+ 255 2DUP BLANK .” ? ” 2DUP
EXPECT -TRAILING PAD С! DROP PAD;
l15
Здесь ключевое слово -TRAILING. Этому слову нужен в стеке начальный адрес и число символов,
так же как словам BLANK и EXPECT; в данном случае указанный адрес и число были запомнены
в стеке с помощью слова 2DUP, непосредственно предшествующего слову EXPECT. Слово -
TRAILING заменяет затем байт длины строки числом символов до первого пробела ”в хвосте” стро¬
ки символов. В действительности слово -TRAILING просматривает байты, отступая от указанного
адреса на указанное в стеке число байтов вперед, и вычитает единицу, если обнаруживает пробел.
Это повторяется до тех пор, пока не встретится первый символ, не являющийся пробелом. Поэтому
число, которое -TRAILING возвращает в стек, является длиной (счетом) строки. Фактически -
TRAILING отнимает пробелы, следующие после строки. Длина строки затем запоминается словом
$IN по адресу ячейки PAD (с помощью PAD C!). На этом заканчивается формирование счетной
строки. Остается с помощью DROP удалить адрес PAD плюс 1, который кладется в стек словом -
TRAILING, и заменить его на PAD, т.е. адрес байта-счетчика.
Заслуживают упоминания некоторые детали. Стандарт Форт-79 допускает, чтобы слово EXPECT
записывало до двух нулей (байтов со значением 0) в конце строки символов. Если ваша версия
Форта делает это (как, например, версия MMSFORTH), то необходимо в слове $IN сразу же после
-TRAILING вставить операцию 2 -. В Форт-83 нулевые байты не разрешаются. Вам нужно прове¬
рить, как устроен ваш Форт. Другая деталь: число символов, собираемых словом EXPECT в Форт-
83 и других версиях, запоминается в переменной SPAN. Это позволяет упростить определение сло¬
ва $IN на Форт-83:
: $IN ( — $адр) PAD 1+ 255 .” ? ” EXPECT
SPAN @ PAD С! PAD ;
Другими словами, на Форт-83 здесь слово -TRAILING не нужно для подсчета длины строки.
Разумеется, слова EXPECT и -TRAILING имеют много других применений. Мы можем увидеть
некоторые их применения из нескольких упражнений.
Упражнения
1. Вспомните слово EMIT (см. гл. 5), а потом дайте определение слова NEWTYPE, используя цикл DO-LOOP.
2. Вспомните слово KEY из гл. 5 и дайте определение слова EXPECT, используя цикл BEGIN...WHILE...REPEAT. Сде¬
лайте так, чтобы ваша версия слова EXPECT запоминала число символов строки в переменной SPAN. Понимаете ли вы, по¬
чему здесь нельзя использовать счетный цикл? Не забудьте предусмотреть возможность появления символа стирания влево
(код 8).
3. Предположим, что слово EXPECT завершает несчетную строку по крайней мере одним нулевым байтом. Определите
слово, которое будет выводить строку на экран, если известен адрес первого символа. Используйте цикл
BEGIN...WHILE...REPEAT.
4. Зарезервируйте 258 байтов в массиве с именем $SPACE, используя слова CREATE и ALLOT. Определите слово GET$,
которое работало бы так же, как $IN, но запоминало бы строку в переменной $SPACE.
5. Определите слово ADD$, которое будет ожидать ввода строки, а затем присоединять ее к концу строки, находящейся в
массиве $SPACE. Не забудьте скорректировать байт-счетчик с помощью слова -TRAILING после того, как строка будет по¬
мещена в переменную. Очевидно, что суммарная длина обеих строк не должна превышать 255 байтов, поэтому, если строка
получится слишком длинной, ее необходимо урезать.
6. Дайте новое определение слова ADD$ под именем ADD$1 такое.чтобы вторая строка игнорировалась, если суммарная
длина обеих строк превышает 255 байтов, т.е. строка в $SPACE не должна изменяться.
7. Просмотрите материал о форматном выводе в гл. 5 и обратите внимание на то, что слово #> оставляет в стеке адрес
несчетной строки и байт-счетчик. Теперь определите слово, которое должно брать из стека число двойной длины, представ¬
ляющее число центов, преобразовывать его в счетную строку вида $123.45 и запоминать строку в массиве $SPACE. Исполь¬
зуйте слово CMOVE.
8. Определите слово LEFT$, которое предполагает наличие в стеке адреса строки, поверх которого находится байт-счет¬
чик. Заданное в стеке число символов должно быть затем перемещено из указанного адреса по адресу PAD + 1, а новая дли¬
на строки должна быть записана в PAD. Слово LEFT$ должно оставлять в стеке адрес новой строки, т.е. PAD. Таким обра¬
зом, если в $SPACE находится строка ”The quick brown fox”, тогда после операций
$SPACE 9 LEFT$
в стеке будет оставлен адрес счетной строки ”The quick”.
116
Ввод с помощью слова WORD
WORD — это слово, преобразующее в счетную строку все, что во входном потоке следует после
него, и возвращающее в стек адрес начала этой строки (байт-счетчик). Слову WORD нужен в стеке
код ASCII байта-разделителя (например, 32 — пробел или 34 — кавычка ”), оно запоминает все,
что встречается, вплоть до этого разделителя. Попробуйте ввести следующее определение
: SHOW-SILLY 34 (") WORD CR COUNT TYPE ;
а затем введите предложение
SHOW-SILLY This is а silly example" <BK>
(Это глупый пример)
Тогда вы увидите на экране текст ”This is а silly example”, повторенный строкой ниже введенного.
Строка ”This is а silly example” была запомнена словом WORD, а затем распечатана с помощью
фрагмента COUNT TYPE. Слову WORD известно, что нужно прекратить запоминание строки, ког¬
да оно встретит в стеке разделитель-кавычку (так как 34 — это код ASCII для кавычки). Если вы
введете
SHOW-SILLY silly example” xyz <BK>
то будет выдано сообщение об ошибке, потому что после того, как будет исполнено слово SHOW-
SILLY, Форт будет пытаться интерпретировать xyz как слово языка Форт. Слово WORD прекраща¬
ет ввод также при нажатии клавиши <BK>. Поэтому
SHOW-SILLY This is а test <BK>
произведет такое же действие. Слово Форт также игнорирует ограничитель в начале строки, т.е. ес¬
ли ввести
SHOW-SILLY ’’’’"’’This is а silly example" <BK>
то вы увидите то же самое, что и в первом примере. А вот пример уже далеко не глупый:
.( 41 WORD COUNT TYPE ; IMMEDIATE
Он представляет собой определение слова Форт-83 .( (точка-скобка). Вам должно быть понятно в
нем все, за исключением слова IMMEDIATE (немедленно), которое просто помечает, что последнее
определяемое слово должно быть исполнено сразу, как только оно встретится, независимо от того,
находится ли оно внутри другого определения через двоеточие или нет. Таким образом, слово .( бу¬
дет выводить на экран все, что следует после него, вплоть до ) (скобки, код ASCII которой равен
41) даже внутри определения-двоеточия.
В большинстве версий строка, которая запоминается словом WORD, помещается в верхней части
словаря. Ее адрес возвращается в стек словом HERE (здесь), и в соответствии с общепринятым в
Форте жаргоном мы будем обращаться к адресу вершины словаря с помощью слова HERE. Новые
слова добавляются в словарь с ячейки HERE, а слова , (запятая), ALLOT (зарезервировать) и др.
резервируют место, начиная с адреса HERE.
Кроме того, слово WORD используется Форт-системой для разбора или разделения слов во вход¬
ном потоке, который помещает выделенные слова в HERE. Поэтому если после ввода строки словом
SHOW-SILLY было введено что-либо еще, то вновь введенная строка будет искажена. Практически
это означает, что нужно сразу использовать то, что введено и запомнено словом WORD, или защи¬
тить до дальнейшего ввода. Попробуйте ввести следующую последовательность операторов:
32 WORD TESTING COUNT TYPE <BK>
Почти наверняка в ответ вы увидите на экране текст ’’TYPE”, а не ’’TESTING”. Причина здесь в
том, что, хотя строка ’’TESTING” была записана словом WORD в ячейку HERE, слово WORD ис¬
пользовалось Форт-системой для интерпретации операторов COUNT TYPE и так как последним,
что встретилось во входном потоке, была строка ’’TYPE”, она и была выведена на экран. На самом
деле, если вы введете
HERE COUNT TYPE
то также увидите на экране текст ’’TYPE”. Строка ’’TYPE” была преобразована в счетную строку и
напечатана потому, что она была запомнена с адреса HERE, когда интерпретатор пользовался сло¬
вом WORD. Если вы этого не увидели на экране, значит, ваша версия Форта — одна из немногих,
которая не запоминает строку, введенную словом WORD, в ячейке HERE. Если после ввода строки
словом WORD происходит ее искажение последующим вводом, то что же хорошего в этом слове?
Можно ответить так: строка должна быть либо защищена, либо перемещена в памяти, прежде чем
будет сделан новый ввод. Например, строку можно было бы переместить по адресу PAD, как в сле¬
дующем слове:
: SAYE-STRING 34 WORD DUP C@ 1+ PAD SWAP CMOVE ;
Теперь введите
117
SAVE-STRING This is a test of SAVE-STRING” PAD COUNT TYPE
Тогда вы увидите на экране текст ”This is а test of SAVE-STRING” (это тест слова SAVE-
STRING). Операция 1 + нужна здесь потому, что нужно переместить не только символы строки, но
также и байт-счетчик. Строка, которая теперь находится в PAD, может быть перемещена куда
угодно, и с ней можно обращаться так же, как 90 строкой, запомненной словом EXPECT в пред¬
ыдущих примерах. Но еще можно очень просто защитить строку, которая вводится словом WORD,
осйбенно если в вашей версии Форта, как в большинстве других, строка помещается на верху сло¬
варя. Можно создать строковую константу, т.е. константу, которая вместо числа содержит строку.
Вот как можно это сделать:
: $CONSTANT CREATE 34 WORD C@ 1+ ALLOT ;
Действие $CONSTANT аналогично действию SAVE-STRING, за исключением того, что строка не
перемещается в памяти, защищается в словаре с помощью слова ALLOT — так же, как в гл. 6 де¬
лается защита массива. Теперь можно использовать слово $CONSTANT следующим образом:
$CONSTANT HOWDYDO Comment allez vous? ” <BK>
(Как дела? /франц./)
Если вы введете
HOWDYDO COUNT TYPE
на экране появится текст ’’Comment allez vous?” (”Как дела?” (франц.)) Не написать ли нам раз¬
говорник, пользуясь языком Форт?
Слова SAVE-STRING, $CONSTANT, HOWDYDO и подобные им слова можно, очевидно, ис¬
пользовать не только для ввода с клавиатуры, но также и с диска. Заметьте, однако, что нельзя
просто ввести
CREATE HOWDYDO 34 WORD Comment allez vous? ” C@ 1+ ALLOT
так как C@ 1+ ALLOT наложится на фразу, которую вы собирались сохранить. Почти во всех слу¬
чаях слово WORD должно использоваться в определениях через двоеточие.
Если в вашей версии Форт строка, вводимая словом WORD, запоминается не в ячейке HERE, то
слово $CONSTANT можно определить следующим образом:
: $CONSTANT CREATE 34 WORD COUNT DUP 1+ ALLOT HERE
SWAP CMOVE ;
Т. e. строка должна быть перемещена оператором CMOVE в HERE, чтобы защитить ее с помощью
ALLOT.
В сочетании со словом WORD часто используется слово QUERY. В Форт-83 это слово не обяза¬
тельное (оно входит в Форт-79), тем не менее имеется почти во всех версиях. Слово QUERY рабо¬
тает почти так же, как EXPECT (оно и определяется через EXPECT), но в отличие от последнего
оно запоминает строку не с того адреса, который вы указываете, или используя указанную длину
строки, а в текстовом сходном буфере (TIB) длиной 80 байтов — специально отведенной области
памяти. Этот буфер используется для хранения входного потока информации с клавиатуры, и слово
QUERY используется Форт-системой как раз для приема входного потока. В Форт-83 и большинст¬
ве диалектов адрес начала текстового входного буфера возвращается словом TIB (от Text Input
Buffer). Попробуйте ввести
TIB 11 TYPE
и вы увидите, что на экран выводится ”Т1В 11 TYPE”. Вы просто вывели 11 символов из тексто¬
вого входного буфера, т.е. то, что вы в него ввели. Теперь попробуйте ввести определение
: SILLY-QUERY TIB 80 BLANK 0 >IN !
” ? ” QUERY CR TIB 80 TYPE ,
Как и $IN, слово SILLY-QUERY делает паузу, чтобы вы что-либо ввели, например фразу ”The
quick brown”, а затем печатает то, что вы ввели. Но, кроме того, оно еще печатает сообщение об
ошибке. Это происходит потому, что после того, как SILLY-QUERY выведет то, что было введено,
Форт пытается интерпретировать все находящееся во входном буфере как Форт-слова и, естествен¬
но, он не может распознать текст ”The”. Вы можете избежать этого, если сразу же после TYPE
введете в определение TIB 80 BLANK, чтобы очистить буфер. Возможно, единственная загадочная
часть определения — это 0 >IN !. Слово >IN — это переменная пользователя, в которой содержится
число, показывающее, на какую глубину использован буфер ввода (см. гл. 15). Хотя это слово не
требуется в большинстве версий, нужно установить переменную >IN в 0 для того, чтобы после
QUERY либо интерпретатор, либо слово WORD выполняли свои действия с начала буфера.
Слово QUERY само по себе мало что дает пользователю (хотя, конечно, оно очень важно для
осуществления ввода в Форт), вместо него обычно используется слово EXPECT. Слово QUERY поч¬
ти всегда используется в сочетании со словом WORD. Слово QUERY заполняет текстовый входной
118
буфер, после чего слово WORD производит разбор текста, т.е. оно разделяет входной поток на
меньшие строки с учетом указанного разделителя. Следовательно, WORD можно использовать для
разделения входного потока на более мелкие части. Попробуйте ввести следующее определение:
: SILLY-ТОО TIB 80 BLANK 0 >IN !
.” ? " QUERY 32 WORD CR COUNT TYPE TIB 80 BLANK ;
Введите SILLY-ТОО и в ответ на вопрос ”?” введите текст ”The quick brown fox”. Тогда в следую¬
щей строке вы увидите, что выводится текст ”The”. Код 32 указывает слову WORD, что разделите¬
лем считается пробел, поэтому слово WORD преобразовало текст ”The” в счетную строку, а
COUNT TYPE вывело эту строку. Конечно, этот пример глупый также, поскольку он не делает ни¬
чего, кроме вывода части того, что вы ввели, на экран. Как и при других применениях слова
WORD, входной поток должен быть запомнен либо защищен раньше, чем определение будет испол¬
нено. Вот два более полезных слова:
: PARSIT TIB 80 BLANK 0 >IN ! .” ? ” QUERY
4 0 DO
44 WORD DUP C@ 1+ PAD 10 I * * SWAP CM0VE
LOOP TIB 00 BLANK ;
SH0WIT 4 0 D0 PAD I 10 * + COUNT TYPE L00P ;
Слово PARSIT разделяет четыре строки, каждая из которых может содержать до 10 символов, кото¬
рые отделены друг от друга запятой (код ASCII 44). Вам должно быть понятно, как оно работает,
по аналогии с другими словами, которые мы определили в этой главе. (Слово PARSIT работает,
только если ввести его с клавиатуры, поскольку слово QUERY работает только с клавиатурой, но не
работает с диском.) В гл. 10 мы увидим, что разбор используется для выделения строковых данных
из блоков.
Упражнения
1. Определите слово $VARIABLE, которое, если ему предшествует число, будет резервировать указанное им количество
байтов в памяти для запоминания именованной строки, т. e.
256 $VARIABLE $SPACE
резервирует 256 байтов для счетной строки в переменной $SPACE.
2. Определите слово $!, которое, если в стеке имеются два адреса $адр1 и $адр2, будет перемещать счетную строку из
адреса $адр1 в $адр2. Тогда если строка запомнена в PAD, то :
PAD $SPACE $!
переместит строку в переменную, определенную в упражнении 1.
3. Определите слово $GET, которое будет перемещать вводимую после него строку в ячейку с адресом, указанным в сте¬
ке. Так, например,
PAD $GET The quick brown fox...”
будет помещать счетную строку ”The quick brown fox...” в память, начиная с PAD.
4. Определите слово $", которое должно помещать следующую после него строку в память, начиная с PAD, и возвращать
в стек адрес, где сохраняется строка. Таким образом ,
$” This is а string."
будет выводить на экран "This is а string” и помещать в стек адрес PAD.
5. Как бы вы запомнили строку, вводимую словом S” в переменную $SPACE, пользуясь словами, определенными в этих
упражнениях?
6. 10 строк запомнены в строковых константах с именами $S1, $S2, ..., $S10. Определите слово $CHOlCE, которое, если
указывается номер, будет печатать соответствующую строку. Например,
4.$CH0ICE
будет печатать строку, хранящуюся в $4. Может быть, вам потребуется вспомнить векторное исполнение из гл. 6.
7. Определите четыре строковые переменные с именами lSTRING, 2STRING и т.д. Теперь определите слово, аналогич¬
ное PARSIT, которое будет помещать четыре строки с разделителем # в эти переменные, а не в разные места после PAD,
как это делало слово PARSIT. (Указание: используйте вектор и $!.)
8. Как вы думаете, что происходит со значением переменной >IN, когда исполняется слово WORD? Модифицируйте сло¬
во, которое вы определили в упражнении 7, чтобы оно печатало значения переменной >IN каждый раз при исполнении сло¬
ва WORD.
9. Определите слово, эквивалентное (, под именем (( . Оно должно работать как внутри, так и вне определения-двоето¬
чия.
119
Расширенный набор строковых операций в MMSFORTH
К настоящему времени мы уяснили, что, хотя Форт сам по себе не обладает широкими возмож¬
ностями работы со строками, в нем есть несколько слов, необходимых для определения практиче¬
ски любого слова для работы со строками, какое вам только потребуется.
Как вы уже видели, многие слова, нужные всем, можно определить и включить в любую версию
Форта. К сожалению, только в некоторых версиях предусмотрены такие слова, расширяющие воз¬
можности языка. Одной из версий является MMSFORTH. Она имитирует большинство операторов
для обработки строк, имеющихся у Бейсика, представляя собой превосходный пример, каким долж¬
но быть хорошее программное обеспечение для работы со строками. Если даже вы не располагаете
версией MMSFORTH, вам следует ознакомиться с его словами; знать о них очень полезно, и они
могут дать вам некоторые идеи для создания слов, которые могут потребоваться.
Мы дадим описание каждого слова MMSFORTH для работы со строками и некоторые примеры их
использования. В некоторых случаях определения слов будут достаточно очевидны, и вы можете
попробовать сами дать их определения. Но мы не будем приводить определения этих слов, потому
что для некоторых слов требуются приемы, которые вы еще не изучали (в частности, программи¬
рование на ассемблере), а также вследствие того, что они защищены авторским правом на
MMSFORTH. В конце концов, авторы MMSFORTH имеют право получать вознаграждение за свою
работу. Сводка слов MMSFORTH для работы со строками приведена в табл.9.1. В некоторых случа¬
ях их действие настолько очевидно, что мы не будем описывать их подробнее. В других случаях мы
дадим примеры применения некоторых слов.
Таблица 9.1. Слова MMSFORTH для работы с символьными строками
(Все строки принимаются счетными.)
$CONSTANT ( —) ( строковая константа)
Используется в форме
$CONSTANT NAME "This will be а constant”
(Это должна быть константа)
Во время исполнения слова NAME (имя) в стек кладется адрес строки. Действие слова такое же, как
слова $CONSTANT, ранее описанного в этой главе.
$VARlABLE ( n —) (строковая переменная)
Резервирует n байтов для запоминания строки в массиве. Используется в формате
256 $VARIABLE NAME
Действие слова NAME такое же, как в случае константы $CONSTANT.
( $адр —)
Выводит на экпан строку, начинающуюся с адреса $адр. Эквивалентно по своему действию группе
операторов COUNT TYPE.
$! ( $адр1 $адр2 —)
Перемещает строку, начинающуюся с адреса $адр1, в область памяти с начальным адресом $адр2.
Начиная с $адр2, должно быть зарезервировано место, достаточное для помещения строки, иначе ре¬
зультат будет искажен.
$” ( — $адр)
Принимает следующую за собой символьную строку (используя в качестве разделителя кавычку '” ’),
возвращает в стек адрес, начиная с которого запоминается строка. Используется в форме
$” This is а string” $.
Будет выведено "This is а string” (Это строка). To же самое, что и слово S”, которое вы опреде-
лилив упражнениях. Строка запоминается с адреса PAD.
IN$ ( -- $адр)
Печатает знак вопроса ”?’’ и приостанавливается, ожидая ввод строки с кавычкой в качестве ограни¬
чителя (’” '). Пример использования этого слова при вводе строки с клавиатуры
IN$ ? This is а string" <BK>
$.
Будет выведено "This is а string”. Применение идентично применению слова $IN, определенного ра¬
нее в данной главе.
LEFT$ ($адр1 n — $адр2)
Помешает n левых символов строки, находящейся по адресу $адр1, в счетную строку, начинающуюся с
адреса PAD и возвращает в стек адрес PAD. Если в строке окажется меньше n символов, то будет по¬
мещена вся строка. Если по адресу FOXY запомнена строка ”The quick brown fox”, то последователь¬
ность операторов
FOXY 9 LEFT$ $.
напечатает ”The quick”.
RIGHT$ ( $адр1 n — $адр2)
Помешает n правых символов строки, находящейся по адресу $адр1, в счетную строку, начинающуюся с
адреса PAD, и возвращает в стек адрес PAD. Если в строке меньше чем n символов, то будет помещена
вся строка. Если по адресу FOXY запомнена строка ”The quick brown fox", то последовательность
операторов
FOXY 10 RIGHT$ $.
напечатает текст ”brown fox”.
MlD$ ( $адр1 n1 n2 — $адр)
120
Помещает n2 символов из середины строки, находящейся по адресу $адр1, начиная с символа номер
n1. в счетную строку, начинающуюся с адреса PAD. Возвращает в стек адрес PAD. Если между позицией
n1 и концом строки окажется меньше n2 символов, то будут помещены все символы, начиная с номера n1
до конца строки. Если по адресу FOXY запомнена строка "The quick brown fox”, то последователь¬
ность операторов
FOXY 5 11 MID$ $.
напечатает текст "quick brown”.
$XCHG ( $адр1 $адр2 —)
Переставляет содержимое с адреса $адр1 с содержимым с адреса $адр2, используя для промежуточно¬
го хранения строки, начинающейся с адреса $адр2, адрес PAD. Начиная с адреса $адр1 и адреса
$адр2, должно быть зарезервировано место, достаточное для размещения большей из двух строк, иначе
могут произойти непредвиденные последствия.
$+ ( $адр1 $адр2 — $адрЗ)
Содержимое адреса $адр2 добавляется (соединяется) к концу содержимого адреса $адр1, результирую¬
щая строка помещается, начиная с адреса PAD, при этомв стек помещается адрес PAD. Если с адреса
FOXYSTART начинается строка ”The quick”, а с адреса FOXYEND находится строка ’’brown fox", то
последовательность операторов
FOXYSTART FOXYEND $+ $.
напечатает текст "The quickbrown fox". Обратите внимание, что между словами quick и brown нет про¬
бела .
$COMPARE ( $адр1 $адр2 — флаг)
Две строки сравниваются посимвольно с учетом алфавитного порядка символов, в стек возвращается
флаг. Если строки равны, то возвращается флаг 0; если строка с адресом $адр1 находится по алфавиту
дальше, чем строка с адресом $адр2, то возвращается +1; если строка с адресом $адр2 находится по
алфавиту дальше, чем строка с адресом $адр1, то возвращается -1. Если по адресу ALPHA находится
строка ”abc”, а по адресу BETA "abd", то последовательность операторов
ALPHA BETA $C0MPARE .
выведет на экран -1, в то время как
BETA ALPHA $C0MPARE .
выдаст 1. Символы, не входящие в алфавит, сравниваются по значению кодов ASCII.
INSTR ( $адр1 $адр2 — n)
Содержимое строки по адресу $адр1 ищется в тексте строки, начинающейся с адреса $адр2. Если оно
не находится, в стек возвращается 0. Если находится в стеквозвращается номер позиции первого
символа строки, хранящейся по адресу $адр2. Если по адресу FOXY содержится строка ”The quick
brown fox”, а по адресу FOXYPART строка "brown”, то последовательность операторов
FOXY FOXYPART INSTR .
выведет на экран 11.
ASC ( $адр — n)
Возвращает в стек значение кода A$CII первого символа строки, хранящейся по адресу $адр1. Если
по адресу FOXY помещена строка "The quick brown fox”, то после
FOXY ASC .
будет выведено 84, т.е. код ASCII буквы Т.
CHR$ ( n — $адр)
Преобразует число n в односимвольную счетную строку, которая содержит символ, код ASCII которого
равен n.
84 CHR$ $- Л..П*
напечатает букву Т. Слово CHR$ можно определить следующим образом:
: CHRS 1 PAD С! PAD 1+ С! PAD ;
INKEY$ ( — $адр)
Опрашивает клавиатуру, и если была нажата клавиша, то по адресу PAD запоминается односимвольная
счетная строка. Если ни одна клавиша не нажата, то в PAD помещается 0. Возвращает в стек адрес
PAD. Обычно слово INKEY$ используется в цикле, пример его применения вы найдете в тексте ниже.
STRING$ ( n1 $адр1 --$адр2)
Создает счетную строку в PAD, состояшую из n1 символов, таких же, как первый символ строки с
адресом $адр1. В стек помещается адрес PAD.
10 $" S” STRING$ $.
выведет строку "SSSSSSSSSS”. Приведенное ниже слово очистит содержимое счетной строки, заместив
его пробелами:
: ERASE-$ ( $addr —) DUP C@ 32 CHR$ STRING$ SWAP $! ;
STR$ ( n — $адр)
Преобразует число n в строку, запоминаемую в PAD, и возвращает в стек адрес PAD.
12345 STR$ $.
выводит ”12345”.
LEN ( $адр — n)
Возвращает в стек длину строки, запоминаемой по адресу $адр. Определение этого слова очень про¬
стое:
: LEN C@ ;
VAL ( $адр —n или dn)
Если начальные символы строки по адресу $адр распознаются как число в текущей системе счисления
(BASE), то в стек помещается это число. Если в числе обнаруживается десятичная точка, то в стек
помещается число двойной длины. Если первый символ не является цифрой, то в стек возвращается 0.
Таким образом, при шестнадцатеричной системе счисления
$” 123AXYZ” VAL . выводит 123A
$" 123.АВС” VAL D. выводит 123ABC и
$” xyz” VAL . выводит 0
$ARRAY ( n 1 r>2 —)
Слово-определитель для создания строковых массивов. Если ввести
29 19 $ARRAY 20STRINGS
121
то последовательность операторов
•2 20STRINGS
возвратит в стек адрес, где помещается строка длиной до 30 символов (n1) и, кроме того, будет за¬
помнен байт-счетчик. Таким образом,
$" The quick brown fox” 2 20STRINGS $!
запомнит строку и
2 20STRINGS $.
в свою очередь, выведет на экран ”The quick brown fox”. Максимальное значение п1 равно 254. Эле¬
менты нумеруются, начиная с нуля, поэтому значение n2 = 19 в вышеприведенном примере резервирова¬
ло место для 20 строк.
2$ARRAY ( n 1 n2 n3 —)
Слово-определитель для создания строковых матриц из n2+1 рядов и n3+1 столбцов, причем каждая
строка содержит до n1 символов плюс байт-счетчик. Если было введено
19 9 4 2$ARRAY 50STRINGS
то
$” The quick brown fox” 5 2 50STRINGS $!
запомнит текст строки. Тогда
5 2 50STRINGS 8 0 50STRINGS $!
создаст ее копию в элементе матрицы в 8-м ряду, 0-м столбце, так что
8 0 50STRINGS $.
выведет строку "The quick brown fox”.
$-lB ( $адр — $адр)
Удаляет пробелы в конце счетной строки, при этом уменьшает содержимое счетчика числа символов
строки на число обнаруженных пробелов. Таким образом, последовательность операторов
$” The quick " DUP $. $-TB DUP $. $.
напечатает
The quick The quickThe quick
Завершающие пробелы были удалены перед вторым словом $. .
Приведенных объяснений должно быть достаточно, чтобы понять действие большинства описан-
ных слов для работы со строками. Однако некоторые примеры помогут подчеркнуть их полезность.
Возможно, наиболее трудным среди этих слов является INKEY$, поэтому лучше всего посмотреть
его действие на примере. Действие слова INKEY$ аналогично действию ?KEY, которое является в
его определении самым главным. Мы определим слово SHOWKEYS, которое просто выводит на эк¬
ран все, что было введено с клавиатуры (в том числе символы возврата каретки влево не один шаг
и перевода каретки), до тех пор, пока на будет нажата клавиша точки ”.”; мы определим это сло¬
во, используя как ?KEY, так и INKEY$.
. SH0WKEYS ( —)
BEGIN (Начало условного цикла)
?KEY DUP (Помещает в стек код ASCII символа или 0,
если клавиша не нажата)
7DUP IF EMIT THEN (Печатает символ, если не 0)
46 = UNTIL ; (Повторяет исполнение*, начиная с
7KEY, если не была нажата клавиша
".’’, код ASCII равен 46)
А вот определение через INKEY$:
: SH0WKEYS ( — )
BEGIN (Начало условного цикла)
INKEY$ DUP $. (Считывает код клавиши в счетную строку
печатает ее. Длина строки равна 0,
если не была нажата клавиша, поэтому
ничего не печатается)
1+ C@ (Извлекает в стек символ из строки)
46 = UNTIL ; (Повторяет исполнение с INKEY$, если не
была нажата ”.’’, код ASCII равен 46)
Второе определение более медленное, чем первое, но поскольку любое из них работает быстрее,
чем кто-либо сможет сделать ввод с клавиатуры, то скорость не является определяющим фактором.
Второе определение короче и понятнее. Но главное преимущество второго определения состоит в
том, что введенный символ запоминается как счетная строка, которая готова для работы с другими
словами, предназначенными для обработки строковых данных.
Приведем несколько более полезный пример. Мы определим слово BUILDTEST, которое обеспе¬
чит возможность непосредственно записывать счетную строку в строковую переменную TEST$
(аналогичное слово вы определили в упражнении, пользуясь словами EXPECT и -TRAILING).
В качестве разделителя мы используем символ #. Сначала определим строковую переменную
255 $VARIABLE TEST$
и установим ее длину равной нулю:
122
0 TEST$ С!
Теперь мы можем дать определение слова BUILDTEST:
BUILDTEST ( —)
WHILE
BEGIN
INKEY$ DUP DUP $.
TEST$ $!
REPEAT ;
$” Г' $COMPARE
TEST$ SWAP $+
(Начало условного цикла)
(Принимает строку-символ с клавиатуры,
копирует ее адрес
и распечатывает ее)
(Сравнивает строку-символ с ”#” и
возвращает 0, если есть ”#”)
(Если "#” не обнаружен, продолжает
цикл, иначе переходит на исполнение
слов после REPEAT и заканчивается)
(Добавляет строку-символ к TEST$,
результат - в PAD)
(Запоминает слитую строку в TEST$)
(Переходит к повторению с INKEY$)
Хотя в данном случае можно описать слово BUILDTEST "обычными” словами Форта, такое оп¬
ределение было бы труднее и для написания, и для понимания. И кроме того, заметьте, что в отли¬
чие от тех слов, которые вы определили со словами EXPECT и -TRAILING, если вы используете
слово BUILDTEST без предварительного ’’обнуления” переменной TEST$, то оно будет присоеди¬
нять новую строку к тому, что уже было в TEST$. Чтобы так же действовало определение с ’’обыч¬
ными” словами, нужно было бы использовать слово ?KEY, при этом определение самого слова
7KEY было бы очень замысловатым. Конечно, вы можете и прямо ввести строку с клавиатуры в
TEST$, если наберете
$” I am filling the string" TEST$ $! (Я заполняю строку)
В упражнениях мы предложим вам описать более общее и более полезное слово BUILD$.
Телефонный справочник
Чтобы практически оценить полезность приведенных слов для работы со строками, мы составим
небольшую программу, которая будет создавать телефонный справочник. Для этого нужно опреде¬
лить три главных слова: STOREPHONE (записать_телефон), которое будет запрашивать имя або¬
нента и номер телефона и запоминать эти данные в массиве, GETPHONE (выдать_телефон), кото¬
рое будет запрашивать имя абонента и выдавать номер его телефона, если абонент найден, и
ERASEPHONE (стереть_телефон) для удаления абонента и номера телефона, чтобы освободить ме¬
сто в справочнике.
Мы используем два массива: 10-байтовый массив AVAILABLE (доступность), в котором каждый
байт будет содержать 0 или 1 в зависимости от того, свободна (0) или занята (1) позиция в спра¬
вочнике; второй массив PHONES (телефоны), представляющий собой строковую матрицу размерно¬
сти 10x2 (10 строчек x 2 столбца), в каждой строчке содержится имя абонента (нулевой столбец) и
телефон (первый столбец). Вначале создадим и инициализируем массивы:
CREATE AVAILABLE 10 ALLOT AVAILABLE 10 0 FILL
создается массив AVAILABLE и инициализируется заполнением его нулями (все элементы справоч¬
ника, или позиции, свободны). При вводе
29 9 1 2$ARRAY PHONES
создается массив PHONES, в котором на каждую запись отводится 30 символов для имени абонента
и номера телефона. Если требуется справочник на большее число абонентов, то нужно резервиро¬
вать большее число байтов в массиве AVAILABLE и большее число строчек в массиве PHONES.
Теперь можно приступить к определению трех слов, нужных нам для работы со справочником:
• ST0REPH0NE ( — ) (Запомнить_телефон)
." Имя абонента?” IN$ (Вводит имя абонента)
PAD 31 + $' PAD 31 + (Запоминает его выше PAD; выдает адрес)
SPACE ." Номер телефона?” IN$ (Вводит номер телефона PAD)
10 0 D0 (Начинает цикл просмотра справочника)
AVAILABLE x i- C@ (Определяет, свободна [0] или занята [1] запись)
0= IF I 1 PH0NES $' (Запоминает номер в элементе 1,1)
123
I 0 PHONES $!
1 I AVAILABLE+C!
(Запоминает имя в элементе 1,0.)
(Запоминает 1, чтобы отметить,
что I-я запись занята)
(Запись произведена, поэтому вы
ходит из цикла и из слова)
(Если дошли до конца записей)
(Не обнаружено свободного места в справочнике)
LEAVE
THEN
I 9 = IF
CR ." Справочник заполнен”
THEN
LOOP ;
После того как с клавиатуры введено имя абонента, его необходимо убрать из PAD в PAD плюс 31,
чтобы освободить место для ввода номера в PAD. Затем с помощью счетного цикла DO-LOOP про¬
сматриваются все позиции справочника для поиска незанятой позиции. Если позиция (i) найдена,
то имя абонента и его телефон запоминаются в элементах 0 и 1 i-й строчки массива PHONES и ис¬
полнение заканчивается. Еслй свободная позиция не найдена, т.е. ни в одной позиции массива
AVAILABLE не встретился 0, то выдается сообщение ’’Справочник заполнен”. Можно было бы оп¬
ределить слово STOREPHONE и без применения слов, обращающихся со строковыми данными, но
это не доставило бы вам никакого удовольствия.
Теперь нам нужно описать слово GETPHONE для вывода обнаруженных в справочнике абонен¬
тов и телефонов:
(Вывести_телефон)
(Вводит строку для поиска)
(Цикл просмотра справочника)
(Берет имя в i-й строке и переставляет
его с искомым именем)
(Возвращает 0, если не обнаружено в
этой позиции, или число, номер
позиции, если имя найдено)
PHONES $. 2 SPACES (Печатает полностью найденное имя)
PHONES $. CR (Печатает номер телефона абонента)
GETPHONE (--)
.” Имя абонента?” IN$ CR
10 0 DO
DUP I 0 PHONES SWAP
INSTR IF
I 0
I 1
THEN
LOOP DROP
(Снимает имя и заканчивает программу)
Обратите внимание, что мы используем оператор INSTR не для определения положения символа в
строке, а только для того, чтобы отметить, найдена ли искомая строка или нет. Кроме того, заметь¬
те, что поиск возможен по любой части имени абонента. Например, если в справочнике имеются
абоненты ’’Godfrey James” и ’’Eugene Godfrey”, то при поиске по фрагменту ”frey” будут выведены
на экран оба абонента. И наконец, нам нужно еще слово для уничтожения ненужных записей и ос¬
вобождения места для новых:
(Стереть_телефон)
(Вводит строку для поиска)
(Поиск, как в GETPHONE.)
(Если найдено . )
(Разрешает ввод в эту позицию)
(Стирает имя, делая длину
записи равной 0)
(Стирает номер, делая длину
записи равной 0)
ERASEPH0NE ( — )
” Имя абонента?” IN$
10 0 D0 DUP I 0 PH0NES SWAP
INSTR IF
0 I AVAILABLE
0 PH0NES
I
С!
С!
0 I 1 PH0NES
С!
THEN
L00P DR0P
(Снимает имя и заканчивает программу)
124
Действия слов GETPHONE и ERASEPHONE очень похожи, но в последнем случае найденное имя
и телефон не выводятся, а стираются. Это производится с помощью записи 0 в длину строки. Ана¬
логично для освобождения найденной позиции в справочнике в массиве AVAILABLE также записы¬
вается 0.. Чтобы использовать эту программу, нужно набрать на клавиатуре имя требуемой функ¬
ции. Но если вы хотите, чтобы программой мог пользоваться любой непосвященный человек, необ¬
ходимо предусмотреть в ней встроенный контроль ошибок, например, чтобы предотвратить ввод
слишком длинного имени или номера телефона. Еще лучше, если программой можно будет пользо¬
ваться, вводя всего одно слово — имя программы, а затем одно из трех возможных слов можно бу¬
дет исполнить по выбору из меню. Поскольку такие улучшения программы дают возможность пока¬
зать пример использования слов для операций со строками, посмотрим, как они могут быть сдела¬
ны.
Вначале мы определим еще одно слово QUITPHONE (закончить_телефоны), которое нужно для
того, чтобы выйти из программы:
: QUITPHONE ." ok” CR QUIT ;
Для исполнения одной из функций программы: трех определенных ранее и QUITPHONE — мы
применим вызов по вектору. Итак, нам нужен вектор
CREATE CHOICE FIND STOREPHONE , FIND GETPHONE ,
FIND ERASEPHONE , FIND QUITPHONE ,
(На Форт-83 вместо FIND нужно использовать ’.) Ну а как мы будем узнавать, какое слово следует
исполнить? Для этого создадим меню, которое будет подсказывать, что нужно ввести ”S”, чтобы за¬
писать номер телефона, ”G” — чтобы его выдать, и т.д. Для приема ответа оператора с клавиатуры
мы используем слово INKEY$, и тогда, если принят ответ ”G”, это будет означать, что мы хотим
вывести номер телефона. Как же теперь можно исполнить соответствующий элемент вектора? Это
можно сделать путем определения позиции буквы ответа (например, ”G”) в строке, по которой мы
будем определять положение функции GETPHONE в векторе. Поэтому нам потребуется строка
CHOICE$ (выбор):
$CONSTANT COICE$ SGEQ”
которая показывает относительное положение слов в векторе. Еще нам потребуется слово для пред¬
ставления меню:
• MENU CR ." НАЖМИТЕ S, чтобы ЗАПИСАТЬ телефон" CR
." НАЖМИТЕ G, чтобы ВЫВЕСТИ телефон” CR
." НАЖМИТЕ E, чтобы СТЕРЕТЬ телефон" CR
.” НАЖМИТЕ Q, чтобы выйти из программы" CR ;
Обратите внимание, как легко составляется меню на Форте. Вот теперь мы можем дать описание
главной программы:
: FINDPHONE ( — ) (Найти_телефон)
MENU BEGIN (Печатает меню и входит в цикл)
CHOICE$ INKEY$ (Вводит адрес строки выбора вариантов;
опрашивает клавишу)
INSTR (Находит позицию символа выбора в
строке CHOICE$ или кладет в стек 0,
если не обнаруживает)
?DUP IF (Если найден один из 4 символов...)
1-2* CH0ISE+@ (Находит слово в векторе и исполняет)
EXECUTE
MENU THEN (Печатает меню и продолжается)
0 UNTIL ; (Бесконечный цикл; прекращается по QUITPHONE)
Снова заметим, что можно определить FINDPHONE и не пользуясь словами для строковых опера¬
ций, — через ?KEY, хотя и значительно более длинным и запутанным исходным текстом, но с по¬
мощью специально сконструированных слов $CONSTANT, INKEY$ и INSTR это сделано значи¬
тельно проще. Вы можете вспомнить, что в MMSFORTH есть слово ACASE (гл.7), которое так же
успешно можно использовать в данном случае, но в других версиях оно не имеет эквивалентов,
между тем слова $CONSTANT, INKEY$ и INSTR относительно несложно определить. Если вы не
располагаете версией MMSFORTH, мы советуем вам написать набор необходимых слов для работы
со строковыми данными.
Еще один способ написать слово, эквивалентное FINDPHONE, состоит в использовании конст¬
рукции IF...THEN, но исходный текст программы получился бы ужасно большим. Поэтому вектор¬
125
ное исполнение значительно предпочтительнее. Написание такой же программы на Бейсике и дру¬
гих языках программирования, конечно, возможно, но представляет собой значительно более труд¬
ную задачу.
У этой программы есть одно существенное ограничение: вы потеряете справочник, как только вы¬
ключите машину. В гл.10 мы разовьем эту программу с той целью, чтобы справочник сохранялся
на диске.
Упражнения
1. Определите слова NEWLEFT$ и NEWRIGHT$, используя MID$.
2. Определите слово $CHAR, которое должно брать из стека код ASCII и создавать счетную строку из одного символа в
PAD, оставляя адрес PAD в стеке.
3. Определите слово lSTCHAR, которое должно возвращать код ASCII первого символа счетной строки, адрес которой на¬
ходится в стеке.
4. Определите слово $SWAP, которое должно переставлять содержимое двух счетных строк, адреса которых находятся в
стеке. Определите это слово на обычном Форте и на MMSFORTH, пользуясь его словами для работы со строками.
5. Найдите слова MMSFORTH, эквивалентные тем, которые были вами определены в упражнениях 1-4.
6. Определите слово с именем BUILD$, которое работает, как BUILDTEST, но, в отличие от него, требует перед собой
указания имени (или $адр) строковой переменной, т.е.
TEST$ BUILD$
должно делать то же самое, что делает одно слово BUILDTEST. (Совет: Запомните адрес в стеке возвратов при первом вво¬
де слова.)
7. Опишите слово, которое работает так же, как BUILD$, используя IN$ и $! (Это очень просто.)
8. Модифицируйте программу для создания телефонного справочника, расширив его до 30 записей. Измените программу
так, чтобы число записей в справочнике можно было просто изменять, изменяя значение одной константы. Почему это луч¬
ше?
9. Определите слово SHOWPHONES, которое должно выводить весь телефонный справочник.
10. Определите слово ERASEALL для стирания всех записей в телефонном справочнике. Оно должно выдавать подсказку:
”Вы уверены, что хотите стереть все? (Y/N)”, прежде чем перейти к исполнению программы. Для распознавания ответа (Y
— да или N — нет) примените функцию INSTR.
Преобразование символьных строк в числа
В гл.5 мы обещали вам, что опишем в данном разделе, как можно вводить числа с клавиатуры по
ходу исполнения программы (функция, которая должна быть стандартной, но тем не менее в стан¬
дартном Форте отсутствует). Весь фокус в том, чтобы вводить числа в виде символьных строк, а за¬
тем преобразовывать их в стеке в числа. Вот как это делается. Наиболее важное слово для этой
операции — CONVERT. Слово CONVERT предполагает наличие двойного (32-битного) слова во
втором и третьем элементах стека и адреса на вершине стека. Адрес должен быть на один байт ни¬
же текста символьной строки, представляющей цифры числа (т.е. адрес должен указывать на байт-
счетчик строки, хотя слово CONVERT игнорирует байт-счетчик). Слово CONVERT затем преобра¬
зует все цифровые символы с начала строки в число (указание знака недопустимо) и добавляет ре¬
зультат к числу двойной длины — ’’зародышу”, находящемуся в стеке. После этого оно оставляет в
стеке адрес первого байта, который не является цифрой, и преобразованное число (в виде числа
двойной длины) ниже этого адреса. Обычно в качестве ’’зародыша” для слова CONVERT использу¬
ется 0 и, кроме того, возвращаемый словом адрес первого нецифрового байта убирается. Слово
CONVERT выполняет преобразование в соответствии с текущим значением основания системы
счисления, т.е. оно будет делать преобразование также и алфавитных символов, если основание
больше 10.
Мы рассмотрим несколько примеров постепенно увеличивающейся сложности, но сначала вы дол¬
жны ввести слова $IN и $CONSTANT, как мы их определили выше в данной главе. Теперь опреде¬
лим
$C0NSTANT $NUM 1234Х”
и введем последовательность операторов
0 0 $NUM CONVERT . . .
Если мы представим для определенности, что $NUM хранится по адресу 1000, то вы должны уви¬
деть на экране:
126
1005 0 1234
Адрес байта-счетчика (1000) игнорируется, и преобразование начинается с адреса 1001; после того,
как оно завершится, адрес символа ”Х” (1005) кладется на вершину стека, а число двойной длины
1234, старшая часть которого равно 0, находится ниже этого адреса. Теперь введите определение
SILLY 0 0 32 WORD CONVERT DROP DROP . ;
и после этого
SILLY 1596
Тогда вы увидите на экране число 1596, выведенное из стека. Если вы попробуете
SILLY -1256
на выходе будет получен 0, потому что слово CONVERT не воспринимает знак ”-” и не делает пре¬
образование. Слово SILLY (глупо) и в самом деле глупое, поскольку оно вводит числа прямо в
стек. Вот несколько более полезное слово:
: #IN 0 0 $IN CONVERT DR0P DROP ;
Слово #IN (число_ввод), которое определено в MMSFORTH, приостанавливает исполнение и, как
и $IN, позволяет вам ввести символьную строку, а затем оставляет в стеке ’’величину” строки в ви¬
де числа одинарной длины. Вы должны самостоятельно определить слово D#IN, которое позволит
вводить числа двойной длины.
Но есть qflHo затруднение в приведенном определении слова #IN; если вы делаете ошибку при
вводе и введете первый символ, например ”Х”, то слово #IN возвратит 0, чего вы, конечно, не хо¬
тели. В связи с этим нужно в слове #IN предусмотреть проверку на наличие ошибки. Вот как это
можно сделать. Забудем слово #IN: FORGET #IN и введем новое определение:
#IN (— n)
BEGIN
$IN DUP 0 0 ROT
CONVERT
SWAP DR0P
R0T 1+ =
RED0
(число_ввод)
(Начинает условный цикл)
(Вводит строку, делает ее копию и
подготавливает ее для CONVERT.)
(Выполняет преобразование)
(Удаляет старшую часть числа)
(Если возвращаемый адрес такой же,
как адрес первого символа, то этот
символ - не цифра)
” DR0P (Убирает ввод и запрашивает новый)
(Если цифра, то продолжает преобразование)
WHILE
REPEAT ,
Теперь введите
HEX #IN
и ответьте на предложение сделать ввод (?) строкой ”XYZ”. Вы увидите сообщение ’’REDO” (вве¬
дите снова) и новый знак вопроса. На этот раз напечатайте в ответ ”12АВ”, а затем выведите ре¬
зультат, вводя . (точку). Попробуйте использовать это определение #IN с другими числами. Не за¬
бывайте возвращаться к десятичной системе счисления после каждого исполнения слова.
Остается еще одна нерешенная проблема: слово #IN не понимает знак ”-”, помещенный слева от
строки символов, если преобразуется отрицательное число. Забудем старое определение #IN:
FORGET #IN и рассмотрим определение, которое будет распознавать отрицательные числа:
#IN (— n )
BEGIN
$IN DUP 1+ C@ 45 =
DUP >R
IF 1+ THEN
DUP 0 0 ROT C0NVERT
R0T ROT
R> IF DNEGATE THEN
DR0P
SWAP R0T 1+ =
WHILE .
REPEAT
REDO " DROP
(ввод_числа)
(Начинает условный цикл)
(Не является ли первый символ знаком "-"?)
(Запоминает флаг в стеке возвратов)
(Добавляет 1 к $адр, пропуская ”-”, если он есть)
(Выполняет преобразование)
(Помещает число двойной длины на вершину)
(Если найден знак "-”, делает отрицательным)
(Удаляет старшую часть числа)
(Если возвращаемый адрес
такой же, как адрес первого символа,
то строка не цифровая)
(Убирает ввод и запрашивает новый)
(Если цифра, то продолжает
преобразование)
127
В этой версии слова #IN сначала просматривается, есть ли в самом начале строки знак ”-” (ми¬
нус); если есть, то адрес начала строки увеличивается на 1, т.е. пропускается символ ”-” и в стек
возвратов помещается флаг истина. После преобразования число двойной длины превращается в
отрицательное, если флаг в стеке возвратов — истина. За исключением той части, где делается
проверка на знак ”-”, определение совпадает с предыдущим. Вы понимаете, что это определение
важное и, возможно, включите его в вашу версию Форта. И кроме того, теперь вы лучше понимае¬
те, как работает слово CONVERT.
В большинстве версий Форта есть еще одно слово, которое по своему действию похоже на
CONVERT. Это слово NUMBER.
Слово NUMBER не стандартное, но оно входит в контролируемый список, т.е. определено, как
оно должно действовать, если имеется. Но беда в том, что некоторые реализации Форта не следуют
стандартам, хотя объявляется, что следуют. Согласно стандарту слово NUMBER предполагает в сте¬
ке наличие адреса строки, оно преобразует эту строку в число двойной длины так же, как и
CONVERT, но, во-первых, не требует в стеке ’’числа-зародыша”, во-вторых, не возвращает в стек
адрес первого не цифрового символа и, в-третьих, в преобразуемой строке может находиться знак
”-”, в таком случае в стек помещается отрицательное число. Вот определение слова NUMBER, со¬
ответствующее его описанию в стандарте:
: NUMBER ( $адр — d или n) 0 0 ROT DUP
1+ C@ 45 = DUP >R +
CONVERT DROP R> IF DNEGATE THEN ;
Вы должны сами понять, как это определение работает, сравнивая его с последним описанием слова
#IN.
Некоторые версии не соответствуют стандарту, в частности они проверяют символ; следующий
после цифровой подстроки, не является ли он десятичной точкой, и если является, то оставляют в
стеке результат в форме двойного числа. В противном случае они выдают результат преобразования
в виде числа одинарной длины. Определением такого слова NUMBER может быть
: NUMBER ( $адр — d или n) 0 0 ROT DUP
1+ C@ 45 = DUP >R + CONVERT
C@ 46 = 0= IF DROP THEN (Если не ”.", то убрать)
DROP R> IF DNEGATE THEN ;
Нетрудно видеть, что последние два определения очень похожи, но в последнем случае результат,
возвращаемый словом CONVERT, проверяется: не содержится ли в нем десятичная точка. Если не
содержится, то старшая часть числа убирается из стека, результат выдается как одинарное число. В
некоторых версиях Форта десятичная точка допускается в любом месте цифровой строки. При этом
они могут принимать или не принимать ее во внимание для того, чтобы выдавать одинарное или
двойное число. Кроме этого, можно устанавливать переменную пользователя (т.е. записывать ”1”),
чтобы отметить, что обнаружена десятичная точка, тогда значение этой переменной дает возмож¬
ность пользователю, если он желает, убирать из стека старшую часть 32-битового числа (как это
делает MMSFORTH).
Если версия содержит слово NUMBER, то оно является важной частью интерпретатора Форт-сис-
темы. Вспомните, что анализ входного потока производится словом WORD, которое оставляет в ре¬
зультате разбора символьные строки. При этом именно слово NUMBER используется для того, что¬
бы цифровые строки превращать в числа, которые кладутся в стек. Эта проблема обсуждается в
гл.15. Чтобы еще лучше оценить слова CONVERT и NUMBER, проделайте несколько упражнений.
Упражнения
1. Опишите три варианта слова D#IN, которые аналогичны трем вариантам слова #IN, данным выше, но оставляют в
стеке двойное число.
2. Определите версию слова #IN, которая оставляет в стеке либо двойное, либо одинарное число, в зависимости от того,
заканчивается или не заканчивается строка десятичной точкой. Определение должно допускать ввод отрицательных чисел и
давать сообщение ’’Введите снова”, если строка не содержит цифр.
3. Определите слово #IN, используя слово NUMBER. Это определение не должно делать проверки строки на нецифровые
символы.
4. Это и последующие упражнения должны убедить вас в том, что и в Форте можно пользоваться инфиксной нотацией.
Если вы определите слово
: SILLY 32 WORD NUMBER DROP ;
128
то в результате
SILLY 526
будет выведено число 526. Здесь слово WORD ввело цифровую строку. Слово NUMBER преобразовало ее в число и, естест¬
венно, напечатало. Учитывая этот пример, напишите слово PLUS, которое будет брать из входного потока число, которое
следует после него, преобразовывать его в число одинарной длины и складывать с числом, которое уже находится в стеке.
Таким образом,
5 PLUS 6 .
должно вывести на экран 11. Может быть, вы определите слово INFIX для упрощения определения слова PLUS, а также
слов, которые встретятся в следующих упражнениях.
5. Теперь определите еще четыре слова: MINUS (минус), TIMES (умножить), DIVIDEDBY (разделить_на) и EQUALS
(равно), которые позволят использовать Форт в качестве калькулятора (правда, только для целых чисел), т.е. в результате
операций
3 PLUS 9 TIMES 2 DIVIDEDBY 4 EQUALS
(3 плюс 9 умножить_на 2 делить_на 4 равно)
вы должны получить на экране 6.
Применяя слова WORD, NUMBER, и др., вы можете полностью переделать саму природу языка. На практике примене¬
ние подобных приемов позволяет написать на языке Форт интерпретатор другого языка, например, Бейсика.
Выводы
Одна из распространенных претензий к языку Форт состоит в том, что он якобы имеет очень сла¬
бые возможности для работы с символьными строками. Не воздерживаются от критики даже те про¬
граммисты, которые работают на языке Форт, когда они пишут что-либо о языке. Мы надеемся, что
вы уже убедились в несостоятельности подобной критики. Форт может быть столь же эффективен в
обработке строковых данных, как и другие языки. С подобной критикой мы уже встречались в свя¬
зи с работой с числами с плавающей запятой. Важная цель стандартов языка Форт состоит в том,
чтобы не навязывать жесткие ограничения, которые могут препятствовать его развитию и гибкости.
С другой стороны, отсюда вытекает, что каждый пользователь должен приспособить язык, чтобы он
соответствовал более общим требованиям, и это задает программисту дополнительную работу. Что
касается авторов, то мы считаем недостатком стандарта то, что он не определяет лучшие возможно¬
сти работать с числами с плавающей запятой и символьными строками. Можно с этим соглашаться
или не соглашаться, но расширением словаря различных версий Форта может значительно увели¬
чить производительность программистов. Почти все работающие на Форте быстро создают свои па¬
кеты программ и слова, которыми можно воспользоваться при необходимости. Одна из целей дан¬
ной книги — помочь вам создать такую коллекцию слов и программ; вы сами можете включить не¬
которые слова из этой главы в свою коллекцию.
Вы уже, вероятно, осознали, что ввод данных с клавиатуры, как числовых, так и текстовых, свя¬
зан с неудобствами. Например, составленный вами выше телефонный справочник стирается, когда
вы выключаете компьютер. В связи с этим нам нужен какой-либо способ запоминать строки и чис¬
ла на диске. Выход из положения состоит в записи в блоки на диске, которые являются предметом
рассмотрения следующей главы.
Глава 10
ХРАНЕНИЕ ПРОГРАММ И ДАННЫХ
Объем памяти микрокомпьютера обычно не достаточен, чтобы хранить в ней все данные и программы, которые нам нуж¬
ны. Поэтому необходимо использовать некоторое устройство массовой памяти. В микрокомпьютерах в большинстве случаев
применяется либо кассетный магнитофон, либо, что более распространено, магнитный диск. Большинство языков программи¬
рования пользуется для хранения программ и данных именованными файлами, управление и обращение с которыми обеспе¬
чивается дисковой операционной системой. Как вы уже знаете, Форт сильно отличается от других языков тем, что сохраняет
программы и данные в блоках.
Чтобы понять дальнейший материал этой главы, вы должны узнать, что такое файлы и как они используются в типичной
дисковой операционной системе, и немного о структуре диска. Файл попросту представляет собой совокупность двоичных
данных, организованных в последовательность байтов, как в памяти ЭВМ. Файл может содержать алфавитно-цифровой текст
в виде кодов ASCII (например, текст этой главы был сохранен в файле после того, как его ввели с помощью процессора тек¬
ста), программу, которая также часто сохраняется в виде кодов ASCII, и данные, которые могут быть записаны либо кодами
ASCII, либо в двоичной форме. Файл можно загружать, если это программа, например, на Бейсике либо к нему может
быть организован доступ из программы (если это данные).
Если вы введете команду ”DIR” с клавиатуры, то большинство операционных систем покажет вам справочник (директо¬
рию) файлов на диске. To, как организуется хранение файла на диске, находится под контролем операционной системы.
Данные записываются на диске с помощью магнитных головок, входящих в привод диска) вдоль концентрических окруж¬
ностей, называемых дорожками. Каждая дорожка разделяется на несколько дуг, называемых секторами, в каждом секторе
сохраняется определенное число байтов (например, в IBM PC с DOS 2.1 на сектор приходится 512 байтов и 9 секторов на
дорожку). Компьютер считывает с диска сектор за сектором. Сектор — это минимальное количество информации, которое за
один раз может быть считано компьютером. В свою очередь, файл хранится в одном или нескольких секторах. Секторы в
файле могут следовать не обязательно подряд, но операционная система прослеживает, какие секторы и в какой последова¬
тельности соответствуют каждому файлу. Если часть файла была изменена, то соответствующие ей секторы перезаписыва¬
ются и изменяются.
В системе Форт, в противоположность описанному, данные и программы хранятся на дисках в блоках, содержащих по
1024 байта; так определяется способ хранения в стандартах. Каждый блок может состоять из одного или более секторов
(Блоки также называют экранами, имея в виду, что находящаяся в блоках информация может быть очень удобно представ¬
лена на экране дисплея в виде 16 строк по 64 символа в строке. Некоторые считают, что экран означает любой блок, кото¬
рый преобразован так, что его можно вывести на экран видеодисплея, даже если он содержит, например, управляющие сим¬
волы.) Как мы уже говорили раньше, Форт может работать либо ”под операционной системой” (т.е., например, Форт вызы¬
вается в операционной системе СР/М или MS-DOS, если ввести команду FORTH), либо может быть сам себе операционной
системой; в этом случае он готов к работе сразу же после того, как машина включена и ’’загружена”. В последнем случае
блоки обычно начинаются с минимального номера на первом приводе дисковода и их последовательная нумерация ведется до
самого большого номера сектора последнего привода (а возможно, еще и привода жесткого диска или устройства записи на
магнитную ленту); так, например, организована система MMSFORTH, и такую организацию мы предполагаем до конца этой
главы. (Имеется также особая версия MMSFORTH, которая работает под управлением операционной системы MS-DOS на
IBM PC.) Если Форт работает с другой операционной системой, то сам Форт представляет собой программу, записанную в
файле данной операционной системы. Такой тип Форт-системы может применять один из нескольких вариантов обращения
с блоками. Один из них предполагает, что на младшем приводе находится операционная система со своими файлами, на
других приводах находятся блоки Форта. Другой вариант состоит в том, что блокам Форта приписывается один или несколь¬
ко файлов и обычно можно управлять выбором файла, с которым работает Форт. При такой организации Форт может иметь
доступ как к данным из файлов операционной системы, так и к данным из блоков, кроме того, может загружать расширяю¬
щие слова, хранимые в виде образа памяти, или компилировать программы, находящиеся в файлах. Нам известна по край¬
ней мере одна версия Форта, HSFORTH, которая как бы игнорирует блоки, она сохраняет и программы, и данные в файлах,
что многие могут считать наилучшим подходом. Но есть множество других способов использования Фортом файлов операци¬
онной системы, которые мы не будем здесь обсуждать; мы будем рассматривать только блоки.
Хотя многие реализации Форта не работают с файлами операционной системы, имеется возможность организовать блоки
Форта в виде файлов данных и программ, подобно тому как операционная система создает файлы из секторов. (В
MMSFORTH имеется программа-утилита, которая позволяет передавать данные из операционной системы в Форт и обратно.)
Взаимодействие Форта с операционной системой — это особая проблема, решение которой зависит от конкретного оборудо¬
вания ЭВМ и операционной системы. Мы не будем обсуждать эту проблему, но покажем вам, как надо работать с програм¬
мами и данными, которые хранятся в блоках Форта. Мы также рассмотрим некоторые простейшие способы использования
нескольких блоков подобно файлу. Сначала мы рассмотрим запись-чтение программы, а затем хранение и запись данных.
130
Вы убедитесь, что отсутствие файлов не является серьезным препятствием для Форта и блоки имеют даже некоторые пре¬
имущества.
Вывод листинга программы и загрузка
Вы уже видели в гл. 1, как можно вывести листинг блоков исходной программы (LIST) и загру¬
зить блок (LOAD) и, может быть, уже редактировали их, пользуясь либо редактором вашей систе¬
мы или редактором, описанным здесь в гл. 12. Сейчас мы рассмотрим более подробно сначала вывод
листинга, а затем загрузку блоков. Для повторения пройденного введите
25 LIST
тогда на экране вы увидите текст блока номер 25 в виде последовательности из 16 строк по 64 сим¬
вола в каждой, пронумерованных от 0 до 15. Во многих версиях Форт имеется возможность вывести
листинг нескольких блоков. Если в MMSFORTH ввести
25 6 PLISTS
то будет выведен листинг шести блоков, начиная с 25-го по 30-й (В некоторых версиях синонимом
этого слова является слово SHOW.) Конечно, все они быстро пробегут по экрану вверх. Слова типа
PLISTS предусмотрены для вывода нескольких блоков на принтер. В некоторых версиях (в частно¬
сти, в MMSFORTH) есть и другие слова, с помощью которых можно получить изящно оформлен¬
ный листинг программы.
Переменная SCR (screen — экран) используется Форт-системой для вывода листинга блоков.
Слово SCR имеется в Форт-79, в Форт-83 это слово необязательное, но в большинстве версий оно
также имеется, и в нем запоминается номер последнего блока, который был выведен. Так, после 25
LIST
SCR @ .
выведет на экран 25. В различных версиях имеются слова для повторения вывода листинга блока,
вывода предыдущего и следующего блока. Если назвать эти слова L, LL (вывести_последний) или
LN (вывести_следующий), то они могут быть определены следующим образом:
: L ( -- ) SCR @ LIST ;
: LL ( -- ) SCR @ 1- LIST ,
и
LN ( — ) SCR @ 1+ LIST ;
Если повторять ввод LN, то будут выводиться последовательно несколько блоков.
Во многих версиях Форта есть слово INDEX, которое показывает первые строки последовательно¬
сти экранов (индексные строки, в которые обычно записывают пояснения о назначении экранов). В
некоторых системах перед словом INDEX должны быть указаны число просматриваемых экранов и
номер начального экрана (так сделано в MMSFORTH), в других — номера начального и конечного
экранов. Таким образом, в зависимости от версии
20 6 INDEX
или
20 25 INDEX
покажут первые строки блоков 21, 22, 23, 24 и 25.
Загрузка блоков словом LOAD так же проста, как и вывод блоков. Если в блоке содержится
текст, который может быть введен с клавиатуры, то этот же текст может быть введен и из блока.
Текст будет интерпретироваться точно так же, как если бы он был введен вручную. Так, если блок
содержит определения слов или текст, который должен быть сразу исполнен, то
25 LOAD
введет текст. Очевидно, слово LOAD можно также использовать, если оно записано внутри блока.
Пусть, например, в блоке 25 содержатся определения, которые являются частью программы, про¬
должающейся в блоке 30. Если в блоке 25 включен текст
30 LOAD
то, когда будет встречен текст 30 LOAD, то будет загружен блок 30, после чего произойдет возврат
к блоку 25, чтобы продолжить ввод того, что еще могло остаться в этом блоке.
Обычно программа располагается на последовательно расположенных блоках. В большинстве вер¬
сий Форта есть нестандартные слова для загрузки последовательности блоков. В MMSFORTH так
же, как слово PLISTS выводит, слово LOADS загружает последовательность экранов. Так, если
программа находится в блоках с 25-го по 30-й, то их можно загрузить путем ввода с клавиатуры:
5*
131
25 6 LOADS
Слово LOADS можно определить следующим образом:
: LOADS ( n1 n2 — ) OVER + SWAP DO I LOAD LOOP ;
Вам должно быть понятно, как оно работает. (В некоторых версиях используются совершенно
другие слова для загрузки, о чем будет сказано дальше.) Слово THRU можно определить так:
: THRU ( n1 n2 — ) 1+ SWAP DO I LOAD LOOP ;
Очевидно, что можно в конце каждого блока последовательности из нескольких блоков помещать
номер следующего загружаемого блока со словом LOAD. Например, в конце блока 25 нужно поме¬
стить 26 LOAD, в блоке 26 — 27 LOAD и т.д. Но во многих версиях предусмотрен более простой
способ. Нестандартное слово --> (следующий_блок) означает ’’загрузить следующий блок”. Как
только будет встречено это слово, будет загружаться следующий блок, даже если после него в пред¬
ыдущем блоке что-либо осталось. Мы узнаем вскоре, как определить это слово.
Слово EXIT производит особое действие, когда оно встречается в блоке (вне определения через
двоеточие). Оно прекращает загрузку блока. Поэтому, если вы хотите загрузить только часть про¬
граммы, вы можете вставить слово EXIT перед той частью программы, которая должна быть проиг¬
норирована.
Кроме слов LIST и LOAD вы должны познакомиться со словом COPY (оно также нестандартное,
но имеется во многих версиях). Слово COPY используется в такой форме обращения:
n1 n2 COPY
чтобы скопировать блок nl в блок n2. В зависимости от версии Форта после слова COPY может по¬
требоваться слово FLUSH, которое указывает, что произведенные в блоке изменения следует сохра¬
нить. Слово FLUSH в MMSFORTH включать в программу копирования обязательно. Слово COPY
удобно для перемещения программы в любое место. Мы предлагаем вам дать определение этого сло¬
ва.
Слово BLK — это переменная, в которой записывается номер блока, загруженного последним
(если ввод производится с клавиатуры, то в BLK записан 0, это означает, что в блоке 0 не может
быть записан исходный код программы). Вы можете определить слово
.BLK ( — ) BLK @ U. ; IMMEDIATE
которое будет показывать на экране номер блока, если он был загружен. Слово IMMEDIATE обес¬
печивает немедленное исполнение слова BLK, независимо от того, включено оно или не включено
в определение-двоеточие. (Мы рассмотрим слово IMMEDIATE более подробно в гл. 15.)
При загрузке блоков используется еще одна переменная >IN. В гл. 9 вы узнали, что >IN указы¬
вает на соответствующий байт во входном буфере, в который поступает входная информация (более
подробно об этом слове смотрите в гл. 15). Если содержимое BLK не равно 0, то это означает, что
ввод производится с диска, тогда >IN указывает на номер байта в блоке, из которого приходит ввод.
В некоторых версиях Форта имеется слово \ (обратная косая черта), которое используется для про¬
пуска оставшейся части строки, т. e. если встречается \, то остаток строки при вводе игнорируется,
поэтому этот знак можно использовать для помещения комментариев. Приведем определение этого
слова:
: \ ( — ) >IN @ 64 / 1+ 64 * >IN ! ; IMMEDIATE
Слово \ заставляет переменную >IN указывать на начало следующей строки, независимо от того,
встречается ли оно в определении через двоеточие или самостоятельно. Теперь мы можем опреде¬
лить слово --> :
: --> ( — ) 0 >IN ! 1 BLK +! ; IMMEDIATE
Вам должно быть понятно, как оно работает.
Упражнения
1. Предположим, что вы определили слово
: —> ( — ) BLK @ 1+ LOAD ; IMMEDIATE
Чем его действие будет отличаться от действия слова --> , определенного выше? (Указание: рассмотрите, что произойдет
после того, как будет загружен следующий блок.)
2. Дайте новое определение слова —> под именем N-->, которое не просто загружает следующий блок, а, кроме того сооб¬
щает на экране ”Блок n загружен”, где n — номер блока, который был загружен. Не пользуйтесь для определения словом —
>!
132
3. Определите слово LISTS, которое будет выводить блоки, сообщая в начале каждого блока его номер ”Блок ххх”, где
xxx — номер блока, если задан номер начального блока и число блоков, которое нужно вывести, т. e. блоки должны выво¬
диться в виде
Блок 25
1
2
3
Для вывода каждого блока потребуется*17 строк. На обычной бумажной странице можно напечатать 66 строк. Определите
слово LISTS так, чтобы между листингами экранов было такое количество пустых строк, чтобы три экрана занимали ровно
66 строк.
4. Иногда требуется, чтобы сообщение об ошибке выдавалось во время загрузки блока и указывало, в каком месте в блоке
обнаружена ошибка. Определите слово с именем ... (уголок), которое указывало бы на местоположение ошибки, т. e. оно
должно выдавать сообщение типа
Block 25 Line 3 Character 53 (Блок Строка Символ)
Вы можете вставить знак ж в любом месте, где вы предполагаете ошибку. Для этого вам потребуется использовать слово
>IN, чтобы определить номер строки и номер символа в строке. Используйте слово IMMEDIATE.
5. При отладке программы возникает необходимость неоднократного изменения содержимого экранов. Если первое слово,
которое определено в программе, : TASK, то осуществить это проще. Пусть ваша программа начинается в блоке 20, вы изме¬
нили ее и хотите удалить старую версию программы. Что вам нужно напечатать на клавиатуре, для того чтобы это сделать?
Скрытые блоки
В некоторых версиях Форта реализована идея скрытых блоков. Очевидно, возможные реализа¬
ции могут быть различными, но одна из них такова: в четных блоках находится код, который нуж¬
но загрузить, в нечетные блоки записаны комментарии о содержимом следующих блоков. Если вы
попытаетесь загрузить скрытый блок, то не сможете это сделать — произойдет ошибка. Кое-кому
может показаться, что применение скрытых блоков — это спорный вопрос, поскольку дисковое про¬
странство должно быть в два раза больше и, кроме того, нельзя одновременно видеть на экране
блок, содержащий код программы, и скрытый блок. Но некоторые.программисты считают такую ме¬
тодику заслуживающей внимания и в некоторых случаях полезной, например если нужны очень
подробные комментарии к программе. Вместо того чтобы рассказывать зам, как это делается, да¬
вайте реализуем скрытые блоки в следующих упражнениях.
Упражнения
1. Определите слово SLOAD, взяв за основу слово LOAD, которое, если указывается четный номер блока, дает сообщение
”Не могу загружать четные блоки” и возвращает управление клавиатуре. Дайте определение, используя слова ABORT” и .”
(точка-кавычка).
2. Определите слово SLIST, используя слово LIST, таким образом, чтобы при запросе на выдачу блока с четным номером
выводился бы следующий блок с нечетным номером. (Разумеется, при запросе на выдачу блока с нечетным номером нужно,
чтобы он выводился.)
3. Определите слово VIEW, которое должно выводить скрытый блок, связанный с загружаемым блоком, т.е. практически
выводить листинг скрытого блока. Таким образом, если вводится 20 VIEW или 19 VIEW, будет выводиться скрытый блок 19.
4. Определите слово SLISTS, которое, если задан номер блока и количество блоков, которое должно быть выведено, будет
выводить четные блоки, на которых записан исходный код программы. При этом первый выводимый блок должен быть та¬
кой же, как в случае слова LIST, определенного в упражнении 2. Перед каждым блоком должен быть указан номер, и если
назначено вывести их на устройство печати, то нужно, чтобы в 66 строках помещалось ровно три экрана. Так, например, 19
5 LISTS должно выводить блоки 20, 22, 24, 26 и 28.
5. Определите слово VIEWS, аналогичное слову SLISTS, из упражнения 3, но выводящее скрытые блоки, т. e. если вве¬
сти
20 5 VIEWS
то будут выведены блоки 19, 21, 23, 25 и 27.
6.Дайте новое определение слова —> с именем S-->, которое вызывает загрузку следующего блока, пропуская имеющиеся
скрытые блоки.
133
Если исходный код размещается у вас на четных и нечетных блоках, то реализовать идею скрытых блоков нельзя — по
крайней мере, не прибегая к сложным программным ухищрениям, которые допускали бы использование "обычных” блоков с
исходным кодом программы.
Загрузка экранов
Наиболее часто Форт подвергается критике за то, что невозможно загрузить Форт-программу из
файлов с помощью операционной системы. Это означает, что вы не можете загрузить программу,
вводя
LOAD "PROG.BAS"
как это делается в Бейсике. (Хотя мы уже выше упоминали о существовании некоторых версий
Форта, которые обеспечивают возможность загрузки Форт-программы из файла операционной сис¬
темы.) Обычно программист должен сам следить, в каких блоках записана его программа, а также
за последовательностью блоков, из которых она должна быть загружена. Как правило, для этого ис¬
пользуется так называемый загрузочный экран, или загрузочный блок. Загрузочный экран предназ¬
начен для того, чтобы связать имя программы с блоками, в которых она размещается, с целью осу¬
ществления загрузки программы по имени. Реализация загрузки экранов может иметь различную
форму.
Проще всего загрузить программу, если все относящиеся к ней блоки соединяются словом —>.
Предположим, что у вас имеется несколько наборов расширяющих слов, которые можно загружать
по мере необходимости. Допустим, это слова для работы со строками, слова арифметики с плаваю¬
щей запятой, декомпилятор и графические слова. В этом случае вы можете скомпоновать блок, в
котором содержатся следующие определения:
• STRINGS 30 LOAD ; (Символьные строки)
FLTPT 40 LOAD ; (Арифметика с плавающей запятой)
'DEC0MP 50 LOAD ; (Декомпиляция)
GRAPHICS 60 LOAD ; (Графика)
Если вы загрузите экран, на котором помещены эти определения, то вы сможете вызывать по вы¬
бору любой из имеющихся наборов, вводя его имя. Например, слово STRINGS загрузит слова для
работы со строками. Разумеется, если блоки в STRINGS не объединены с помощью —>, то можно
определить слово:
: STRINGS 20 5 LOADS ;
или
: STRINGS 20 LOAD 23 LOAD 28 LOAD ;
если программа находится не в последовательных блоках. Идея состоит в том, чтобы определить
слово LOADS, которое сильно отличается от рассмотренного нами выше слова LOADS. Новое слово
LOADS позволяет дать определение слова, которое после его ввода будет загружать блок. Вот опре¬
деление этого слова:
: LOADS ( n — ) CREATE , D0ES> @ LOAD ;
Если это определение использовать, например, так:
30 LOADS STRINGS
40 LOADS FLTPT
50 LOADS DEC0MP
60 LOADS GRAPHICS
то слово LOADS определит слова STRINGS, FLTPT, DECOMP и GRAPHICS. Поэтому если ввести
одно из этих слов, то будет загружен соответствующий ему блок. _Пока вы еще не знаете слово
DOES>, поэтому вам может быть не вполне понятно, как работает слово LOADS, но если вы про¬
смотрите гл. 6, где рассматривается слово CREATE, то поймете, как в данном случае работает
DOES>. Вкратце, слова, заключенные между CREATE и DOES>, определяют, что происходит при
исполнении слова LOADS, в то время как слова, находящиеся между DOES> и ;, описывают, что
должны делать вновь определенные слова. В данном случае они извлекают число — номер блока из
слова, подобного переменной, и затем загружают соответствующий блок. Конструкция
CREATE...DOES> порождает слова, которые создают новый класс слов, о чем мы более детально
будем рассказывать в следующей главе.
Вернемся, однако, к загрузке блоков. Мы можем создать блок-справочник, который будет напо¬
минать о названиях программ и наборов расширяющих слов. Предположим, что вы хотите иметь
134
справочник блока 10, причем загрузочный экран имеет номер 20. Последнее определение в блоке
20 пусть будет таким:
: DIR 10 LOAD ;
при этом пусть в блоке 10 находится
.” Расширения, определенные в блоке 20" CR
CR
." STRINGS” CR (Символьные строки)
.” FLTPT" CR (Арифметика с плавающей запятой)
.” DEC0MP" CR (Декомпиляция)
." GRAPHICS" CR (Графика)
(В Форт-83 вместо оператора .” используется .( ). Если ввести DIR, то после загрузки загрузоч¬
ного экрана вам будут представлены возможности выбора одного из наборов расширяющих слов. В
некоторых версиях Форта, в частности в MMSFORTH, вам предоставляется возможность указать,
какой из блоков Форта нужно загрузить. Если на одном блоке не хватает места для справочника,
нужно указать, как связаны вместе все блоки.
Можно работать с блоками многими другими способами. Некоторые из них мы попробуем приме¬
нить в упражнениях.
Упражнения
1. Пусть у вас имеется игровая программа, которая начинается в блоке 100. Каким образом можно использовать констан¬
ту, чтобы можно было загрузить программу, вводя
PACF0RTH LOAD ?
2. Определите новое слово для загрузки GET, которое загружало бы программу с помощью
GET PACF0RTH
3. Если блоки программы расположены не последовательно, то можно для хранения их номеров использовать массив,
первым элементом которого будет число блоков. Так, если PACFORTH находится в блоках 30, 33, и 36, то массив можно
определить так:
CREATE PACF0RTH 3 , 30 , 33 , 36 ,
Опишите слово LOADIT так, что если вы вводите
PACF0RTH L0ADIT
то будет загружена программа PACFORTH. (Указание: используйте цикл DO...LOOP.)
4. Большим неудобством в Форте может быть слежение за тем, где находятся программы, особенно если они не находятся
в последовательных блоках. Определите слово SHOWBLOCKS (показать__блоки) так, что если вы вводите
PACF0RTH SH0WBL0CKS
то увидите
30 33 36 ok
5. Для слежения за номерами блоков нескольких программ можно также использовать массив типа того, что мы примени¬
ли в упражнении 3. Предположим, что у вас имеются массивы номеров блоков программ lPROG, 2PROG и 3PROG. Создай¬
те массив
"CREATE #PR0GS 5 , 1PR0G , 2 PR0G , 3PR0G ,
Теперь определите слово ?BLOCKS (в_каких_блоках?) так, что если вы напечатаете
#PR0GS 7BL0CKS
то увидите что-нибудь вроде
1 23 25 26 27
2 31 33 39
3 55 56 57 58 60
Надеемся, что в действительности вы никогда не устроите такой беспорядок в ваших блоках.
6. Определите слово LOADEM так, что если вы напечатаете
#PR0GS 3 L0ADEM
то будет загружена программа 3PROG из предыдущего примера.
Эти упражнения могут дать вам первоначальную идею для организации блоков в файловоподобные структуры. Мы рас¬
смотрим детальнее данный вопрос в данной главе дальше.
135
Работа с содержимым блоков
Очевидно, что нужно иметь способ, позволяющий манипулировать содержимым любого блока.
Это должен уметь делать редактор. Возможность управления содержимым блоков требуется также
для извлечения данных из блоков. Если вы выбираете блок (n), который содержит какой-то текст,
например определения слов, и введете
n BLOCK 1024 TYPE
то увидите содержимое этого блока, правда выведенное не очень красиво. Слово BLOCK переносит
содержимое блока в буфер блока область памяти размером 1024 байта, и оставляет адрес буфера в
стеке. После этого 1024 TYPE печатает на экране содержимое буфера. Теперь вы можете делать
всевозможные полезные действия с содержимым того блока, которое было перенесено в память сло¬
вом BLOCK. Если напечатать
n BLOCK CR 64 TYPE
то вы увидите на экране первые 64 символа из блока n, т.е. его первую строку. Последовательность
действий
n BLOCK 64 + CR 64 TYPE
вызовет печать второй строки.
Находящаяся в блоке информация может быть использована, если ее предварительно поместить в
память словом BLOCK, с этого момента с ней можно обращаться как с данными, находящимися в
памяти. Например, вот определение слова LIST:
: LIST ( n — ) CR BLOCK
16 0 DO
I 2 .R SPACE
DUP I 64 * + 64 -TRAILING TYPE CR
LOOP DROP ;
Вам нетрудно понять, как оно работает. Мы должны здесь упомянуть, что в некоторых версиях
Форта имеются операторы, позволяющие обмениваться содержимым блоков не в буферах, а в спе¬
циально выделенной области памяти. Так, в MMSFORTH
50 PAD RBLK
считает в память информацию, содержащуюся в блоке 50, помещая ее в память, начиная с адреса
PAD, в то время как
50 PAD WBLK
запишет 1024 байта из памяти с адреса PAD в блок 50. С помощью этих двух слов в MMSFORTH
описываются слово BLOCK и некоторые другие слова для обращения с буферами блоков.
Предлагаем вам поэкспериментировать с внесением изменений содержимого блоков в нескольких
упражнениях, но сначала мы должны описать, как работает блочный буфер. Выберите два блока
для вывода (назовем их nl и n2). Теперь напечатайте nl LIST, а после этого n2 LIST. Понаблю¬
дайте за поведением дисковода, следя за обращением к нему, и снова напечатайте nl LIST. Вывод
листинга произойдет без участия дисковода. Почему? В Форт-системе имеется по крайней мере два
буфера блока (а в некоторых и больше). Один из блоков вы загрузили в буфер с помощью nl LIST,
другой командой n2 LIST — в следующий буфер. Когда во второй раз вы напечатали nl LIST,
Форт быстро определил, что этот блок уже находится в памяти, поэтому он не сделал попытки за¬
грузить его снова с диска. Если блок уже помещен в память словами BLOCK, LIST, LOAD и т. п.,
то он будет загружаться только в том случае, если его еще нет в памяти. Подобное использование
буферов блока называется иногда хранением в виртуальной памяти, поскольку в некотором смыс¬
ле диск является частью памяти компьютера. Способ использования буферов диска называют также
кэшированием, т.е. хранением в памяти часто используемой информации с диска, не без загрузки
ее с диска каждый раз, когда требуется доступ к этим данным. Кэш-диск сокращает число обраще¬
ний к дисковому устройству, значительно ускоряя исполнение программы, когда одни и те же дан¬
ные требуются многократно. Во многих версиях Форта число буферов диска увеличено минимум на
2. Если вам придется работать с большим количеством часто используемых данных и вам не хватит
двух буферов блоков, вы можете увеличить их число. Попробуем сделать несколько экспериментов,
манипулируя с информацией из блоков, чтобы проследить, как используются буферы. Выберите три
блока, содержимым которых вы не дорожите, и поэтому их можно переписывать (мы назовем их
nl, n2 и n3 и будем считать, что у нас есть только два буфера блоков). Теперь попробуйте сделать
10 n1 BLOCK !
а потом
n1 BLOCK @ .
136
выведет на экран число 10. Первый элемент в блоке nl вы изменили на 10. Теперь напечатайте
20 n2 BLOCK !
после этого
30 n3 BLOCK !
а затем
n 1 BLOCK @
Число 10, которое вы занесли в первый элемент блока nl, куда то пропало! Что произошло? Ког¬
да вы поместили число 30 в блок n3, то поскольку блок n3 был загружен с диска в первый блочный
буфер, то его содержимое наложилось на содержимое ранее находившегося здесь блока nl. Поэтому
перед извлечением первого элемента блока nl произошла перезагрузка nl, но с его исходным со¬
держимым, а не с числом 10 в первом элементе. Когда в блочный буфер вводится новый блок, то он
попадает в последний использованный буфер. Как же в таком случае сохранить измененные данные
из буфера на диске? Изменения в буферах диска могут быть сделаны постоянными, если вы исполь¬
зуете слово UPDATE. Попробуйте проделать эксперимент заново, но после каждого изменения,
внесенного оператором записи !, напечатайте UPDATE. Теперь, когда вы во второй раз введете
n 1 BLOCK @ .
то увидите, что в первом элементе блока nl было запомнено число 10. UPDATE помечает, что бу¬
фер нужно сохранить на диске, чтобы в противном случае на его содержимое не могло наложиться
содержимое другого блока. Другими словами, оно делает произведенные изменения постоянными,
так что если используемый в последний раз буфер будет переписываться, то перед этим его содер¬
жимое должно быть сохранено на диске. Любые изменения содержимого буфера блока должны
быть сделаны постоянными, прежде чем он снова будет использован, с помощью слова UPDATE.
Слово UPDATE работает очень быстро, поэтому нет никаких препятствий применять его почаще,
не опасаясь перестараться. Но что будет с блоком, буфер которого не был использован вторично до
выключения компьютера, а изменения были объявлены постоянными? Так как блочный буфер со¬
храняется на диске только в том случае, когда он используется повторно, то внесенные изменения
будут утрачены. Можно заставить Форт-систему записать на диск все буферы, объявленные изме¬
ненными, пользуясь словами FLUSH или SAVE-BUFFERS, которые либо являются синонимами,
либо очень близки по назначению в зависимости от версии Форта. Поэтому после того, как закон¬
чилась программа или процедура, которая произвела изменения в блоке, нужно использовать одно
из этих слов. Между словами FLUSH и SAVE-BUFFERS имеются некоторые тонкие различия, за¬
висящие от версии языка. В Форт-83 оба слова производят запись содержимого всех обновленных
блоков на диске и снимают признак внесения изменений, но если слово FLUSH отменяет приписы¬
вание буферов конкретным номерам блоков, то слово SAVE-BUFFERS может делать или не делать
это в зависимости от реализации. Поэтому если используется слово SAVE-BUFFERS, то можно из¬
менить содержимое буфера (не используя слова BLOCK для загрузки нового блока), снова объявить
изменения постоянными словом UPDATE и сохранить буферы на диске словом FLUSH. Вы можете
экспериментально установить, как работает ваша версия Форта со словом SAVE-BUFFERS. В
Форт-79 обязательным является только слово SAVE-BUFFERS, причем стандарт не оговаривает,
должно ли оно отменять назначение буферов конкретным блокам. В большинстве версий Форт-79
имеется также и слово FLUSH, которое практически является синонимом SAVE-BUFFERS. Незна¬
чительные различия этих слов в Форт-83 в большинстве случаев не имеют значения, и ими можно
пренебрегать.
Можно ли после того, как вы произвели изменения в блоках и объявили их обновленными, пере¬
думать и отменить признак обновления? Вы можете это сделать, используя слово EMPTY-
BUFFERS. Это слово понимает, что запоминать буферы на диске не нужно, и отменяет назначение
буферов блокам, а в некоторых версиях оно, кроме того, производит заполнение буферов нулями
(байтами, имеющими значение 0) или пробелами с кодом ASCII 32.
Слово EMPTY-BUFFERS в Форт-83 не обязательное, поскольку стандартной программе не разре¬
шается изменять содержимое буфера блока до тех пор, пока не будут спасены на диске предыдущие
изменения. Словом EMPTY-BUFFERS следует пользоваться осмотрительно, поскольку можно поте¬
рять ценную информацию, не сохранив ее предварительно на диске. Лучше всего поучиться рабо¬
тать с блоками и буферами блоков, проделав несколько упражнений.
137
Упражнения
1. Определите слово .LINE, которое будет показывать на экране строку заданного блока, если в стеке на вершине указы¬
вается номер строки, а второй элемент содержит номер блока.
2. Определите слово INDEX под именем NEWINDEX, используя слово .UNE.
3. Определите слово LIST под именем NEWLIST, используя слово .LINE.
4. Определите слово BLXiNE, которое будет заполнять строку 64 пробелами (код ASCII 32) t если задан номер блока и но¬
мер строки. Нужно ли делать UPDATE?
5. Слово TL (напечатать_строку), которое является словом MMSFORTH, выводит на экран ряд строк с номерами, так же
как и LIST, выбирая их из блока, номер которого содержится в SCR. Это значит, что
5 SCR ! 9 11 TL
будет выводить строки с 9-й по 11-ю из блока 5. Определите слово TL с именем NEWTL. Определите также NEWLIST под
именем NEWLIST1, используя TL.
6. Определите слово CLEAR-BLOCK, которое, если перед ним в стеке задан номер блока, заполнит его пробелами (код
ASCII 32).
7. Слово PP в некоторых версиях Форта позволяет изменить содержимое строки, попросту печатая ее новое содержимое.
Например,
32 5 PP Это новое содержимое этой строки <BK>
изменит содержимое пятой строки в блоке 32 на текст ”Это новое содержимое этой строки”. Определите слово PP. (Указа¬
ние: Используйте 0 WORD.)
8. Определите слово COPY, назвав его NEWCOPY.
9. Очень полезно слово, позволяющее копировать ряд блоков. Определите слово <COPIES, которое должно применяться в
форме
200 250 5 <C0PIES
для того, чтобы были скопированы 5 блоков с 200-го по 204-й в блоки с 250-го по 254-й.
10. В том случае, когда область, в которую копируется группа блоков, перекрывается с областью, из которой производится
копирование, слово <COPIES действует аналогично слову CMOVE. Определите слово для копирования группы блоков
COPIES>, которое действует по аналогии с CMOVE>.
11. Можете ли вы определить новое слово, которое производит копирование "вперед” (<COPIES) или "назад” (COPIES>)
в зависимости от того, перекрываются или не перекрываются области исходных блоков и область назначения скопированных
блоков?
Хранение данных в блоках
Мы уже несколько раз говорили о том, что поскольку Форт в большинстве своих версий не ис¬
пользует файлы операционной системы, то хранить данные, как в файлах, хотя и трудно, но все же
возможно. Практически хранение данных в блоках обладает большей гибкостью, чем хранение в
файлах, хотя следует признать, что нужно приложить некоторые усилия, чтобы вести учет блоков
и связей между блоками. Более того, можно сконструировать файлы, основанные на концепции
блоков, и директорию (справочник), которая используется так же, как директория операционной
системы. Сначала рассмотрим хранение данных. Простейший способ запомнить данные в блоках со¬
стоит в том, чтобы поместить массивы в буферы диска, откуда их можно переместить для хранения
на диск. Предположим, что у вас имеется массив, который вы создали таким образом:
CREATE TESTARRAY 20 , 26 , 326 , 999 , 228 ,
т.е. 10-байтовый массив с пятью 16-разрядными числами. Вы можете сохранить содержимое массива
на диске с помощью
TESTARRAY 50 BL0CK 10 CM0VE UPDATE
а для извлечения данных из массива с диска можно использовать
50 BLOCK TESTARRAY 10 CM0VE
Конечно, из всего блока мы использовали всего 10 байтов. Вы можете сохранить на диске в каж¬
дом блоке до 102 таких 5-элементных массивов с номерами 0 — 101. Для этого определим сначала
две переменные:
VARIABLE ST0RBLK VARIABLE ARRLEN
и инициализируем их:
50 ST0RBLK ! 10 ARRLEN !
Теперь определим слова для записи массивов на диск:
: ARRAYPUT ( адр n — ) (Поместить_массив)
138
STORBLK @ BLOCK (Помещает блок в буфер)
SWAP ARRLEN @ * + (Рассчитывает размер места
для хранения)
ARRLEN @ CMOVE UPDATE; (Перемещает массив в блок)
Для примера, при вводе
TESTARRAY 5 ARRAYPUT
содержимое TESTARRAY будет запомнено в массиве номер 5 блока STORBLK. На практике необ¬
ходимо предусмотреть некоторую проверку на возможность появления ошибок, как, например, по¬
пытку записи данных после конца блока. Если рассматривать блок 50 как файл, то можно назвать
входящие в него 10-байтовые массивы записями. Не забывайте, что для любой записи на диск тре¬
буется исполнить слово FLUSH. В действительности чаще всего не требуется запоминать результа¬
ты в массивах перед записью их в блок. Приведем пример, в котором будем рассматривать три бло¬
ка как три файла записей данных наблюдений врачом пациентов. Пациентам присвоены номера 0
— 512, в записи о каждом пациенте должны быть указаны его вес, систолическое и диастолическое
давление крови. Эти данные нужно ввести в три последовательно расположенных блока, начинаю¬
щихся с адреса PATBLOCK, печатая вес, систолическое давление, диастолическое давление, номер
пациента и слово PD (ввести_данные_пациента), т.е. последовательность
125 132 86 92 PD
должна записать вес 125 фунтов в первый блок, систолическое давление 132 во второй блок и ди¬
астолическое давление в третий блок для пациента номер 92. Эти данные должны попадать в 184-й
и 185-й байты каждого блока (2 x 92 и 2x92 + 1). Мы выбрали такое загадочное короткое имя слова
PD только для того, чтобы облегчить ввод для оператора. Для начала нам нужна константа
PATBLOCK:
50 CONSTANT PATBLOCK
Вот как можно определить слово PD :
■ PD ( n1 n2 n3 — )
2 * >R (Сохраняет смещение в блоке)
PATBLOCK 2+ BLOCK R@ + !UPDATE (Записывает систол, давление)
PATBLOCK 1+ BLOCK R@ + !UPDATE (Записывает диастол, давление)
PATBLOCK BLOCK R> + !UPDATE ; (Записывает вес)
Как видно из описания, слово PD будет одновременно стирать существующие записи и записы¬
вать новые. Не забудьте о том, что в конце записи необходимо сделать FLUSH.
Теперь мы можем определить слова для извлечения данных. В качестве примера приводим слово
SD (от Show_Data — показать_данные):
SD ( n — )
2 * >R
CR R@ 2/ . ’’Номер пациента”
CR R@ PATBLOCK BLOCK + @ .” Вес"
CR R@ PATBLOCK 1+BL0CK+ @ ."Систолическое давление".
CR R@ PATBLOCK 2+BL0CK+@ .’’Диастолическое давление".СР ;
Если ввести с клавиатуры
18 SD
то мы увидим что-нибудь вроде
Номер пациента 18
Вес 192
Систолическое давление 148
Диастолическое давление 76
ok
Теперь посмотрим, как можно получить средние показатели по всем пациентам (мы только на¬
чнем решение этой задачи, предоставляя вам закончить ее в упражнениях). Прежде всего нужно
убедиться, что в блоках не содержится ничего, кроме данных о пациентах, т.е. все остальные байты
должны содержать нули. Это сделать просто, вводя
50 BL0CK 1024 0 FILL UPDATE
и то же самое повторить с блоками 51 и 52. Теперь можно ввести данные с помощью PD. Тогда
сумму содержимого каждого блока можно найти, используя слово
. SUMBL0CK ( n — )
BLOCK 0 0 ROT 512 0 DO
139
DUP I 2 * + @ SWAP >R 0 D+ R>
LOOP DROP ;
Теперь, если вы введете
PATBLOCK SUMBLOCK D.
то увидите на экране сумму всех весов. Обратите внимание, что мы должны использовать числа
двойной длины и сложные манипуляции в стеке, так как суммарный вес может оказаться больше
65 535 фунтов (считая, что средний вес каждого пациента больше 127 фунтов). Предлагаем вам
продолжить решение в следующих упражнениях.
Упражнения
1. Переделайте слово ARRAYPUT таким образом, чтобы при попытке записать какую-либо часть массива после конца
блока выдавалось сообщение об ошибке и происходил уход из программы.
2. Предположим, что у вас есть два 10-байтовых массива, lARRAY и 2ARRAY, которые вы хотите записать в последова¬
тельных блоках точно так же, как мы записывали TESTARRAY. Определите слово PUTARRAYS, которое, если ввести:
1ARRAY 2ARRAY 5 PUTARRAYS
запомнит эти два массива последовательно друг за другом, начиная с байта номер 100 (5 x 20 - 100). Зачем может потребо¬
ваться запоминание массивов попарно?
3. Определите заново слова PD и SD так, чтобы ни ввод, ни вывод не могли бы произойти после конца блока.
4. Создайте переменную CNT и модифицируйте программу SUMBLOCK так, чтобы она инкрементировала значение
CNT. В CNT должно накапливаться число ненулевых записей (т. e. фактически число записей пациентов) после исполнения
слова SUMBLOCK. Не забудьте в начале SUMBLOCK обнулить переменную CNT.
5. Определите слово AVE (среднее), используя CNT и SUMBLOCK так, чтобы в стек помещались средние значения каж¬
дого типа данных.
6. Можно выделить для счета пациентов первую ячейку блока вместо отдельной переменной. Каждый раз, когда данные
добавляются или удаляются, должно происходить изменение счетчика. Преимущество этого состоит в том, что счет произво¬
дится при вводе данных, а не при подсчете статистики, поэтому допустимо вводить и нулевые данные (в предыдущем случае
слово SUMBLOCK не должно было считать их). Пусть у вас есть блок с данными о весе пациентов, причем в первой ячейке
блока содержится количество пациентов, а в остальных — значения весов. Определите слово AD (Add_Data- добавить^цан-
ные) таким образом, чтобы при каждом исполнении AD в конце массива добавлялся вес, а счетчик увеличивался бы на 1.
7. Определите слово DD (удалить^данные), которое, если указана позиция, удаляет данные из нее, уменьшает на едини¬
цу счетчик пациентов, а данные, находящиеся выше, перемещаются словом CMOVE вниз. При этом, конечно, необходимо
изменить нумерацию пациентов.
8. Определите вместо SUMBLOCK (SB) и AVE (AV) новые слова, которые так же, как в упражнениях 4 и 5, должны
подсчитывать суммарный и средний вес, используя для счета пациентов первую ячейку блока.
9. Модифицируйте слова, определенные в упражнениях 6 — 8, назвав их ADS, DDS, SBS и AVS, таким образом, чтобы
кроме записи в первой ячейке числа пациентов они записывали бы во второй и третьей ячейках суммарный вес в виде числа
двойной длины.
10. Часто оказыйается полезным сравнение двух блоков на идентичность их содержимого. Если суммы всех байтов перво¬
го и второго блоков равны, то, вероятно, они содержат одинаковую информацию. Такую сумму называют контрольной. Оп¬
ределите слово CHKSUM, которое должно подсчитывать контрольную сумму. Чтобы производить сравнение, не обязательно
использовать числа двойной длины, хотя возможность переполнения не учитывать нельзя. Почему для данной задачи можно
пользоваться 16-битовыми числами? Определите также слово ?BLK- , чтобы при вводе
50 55 9BLK=
в стек помещался флаг истина, если контрольные суммы равны, и ложь в противном случае.
Хранение символьных строк в блоках
До сих пор мы рассматривали хранение в блоках только чисел, представленных в двоичной фор¬
ме. Если вы попробуете вывести на экран листинг блока, на котором были записаны числа, то уви¬
дите какую-либо чушь. Однако так же успешно, как и числа, в блоках можно хранить строки (и
числа, представленные как строки). Мы рассмотрели в гл. 9 создание телефонного справочника, со¬
держимое которого, однако, не сохранялось при выключении компьютера.Теперь мы можем сохра¬
нить содержимое справочника на диске.
Для справочника мы определили два массива, в которых хранились данные: числовой массив
AVAILABLE, содержавший информацию о занятости (доступности) позиций спргвочника, и строко¬
вый массив имен абонентов и их телефонов. Первый массив был длиной 10 байтов, второй состоял
140
из 20 записей, каждая из которых имела длину 30 байтов, т.е. занимал всего 600 байтов. Вся эта
информация помещается в один блок (при этом часть блока будет незаполненной). Записать дан¬
ные в блок очень просто. В первых 10 ячейках блока можно разместить первый массив (доступных
позиций справочника), а массив имен абонентов и телефонов — в следующих 600 байтах блока.
Мы предполагаем, что слова для работы со справочником блока (директорией) определены. Прежде
чем обратиться к ним, определим константу
50 CONSTANT PH0NEBL0CK
(мы предполагаем, что данные будут храниться в блоке номер 50). Теперь после определения мас¬
сивов опишем слово
: SAVEPH0NES ( — ) (Запомнить_телефоны)
AVAILABLE PH0NEBL0CK BL0CK 10 CM0VE
PH0NES PH0NEBL0CK BLOCK 10 + 600 CMOVE
UPDATE FLUSH ;
и, кроме того, слово
. FETCHPH0NES ( — ) (Выдать_телефоны)
PH0NEBL0CK BLOCK AVAILABLE 10 CM0VE
PH0NEBL0CK BL0CK 10 + PH0NES 600 CM0VE .
Если после загрузки программы ввести слово FETCHPHONES, то в память будет введен послед¬
ний (обновленный) телефонный справочник. Больше того, если слово FETCHPHONES было послед¬
ним в загруженных блоках, то все массивы окажутся автоматически инициализированными. Теперь
если в справочнике производятся изменения, то, чтобы запомнить его содержимое на диске, вам
нужно только ввести слово SAVEPHONES. Но не будем торопиться. В гл. 9 мы создали два слова, с
помощью которых можно было изменять записи в справочнике: STOREPHONE и ERASEPHONE.
Можно сделать так, что справочник будет записываться автоматически, если внести простое изме¬
нение в определения этих слов, а именно включить в конце определения слово SAVEPHONES (пе¬
ред ;). Тогда если справочник изменялся, то он будет автоматически обновляться на диске. Теперь
можно выключать компьютер в любой момент без потери данных. Таким образом, можно сделать
справочник составной частью вашего Форта. И все же данный пример фактически не так уж много
разъяснил вам, как работать со строками в блоках. Единственное, что мы сделали, — это записали
в массивы в блоки. Но делать со строками можно гораздо больше.
Для разбора (разделения) слов, содержащихся в блоках на диске, можно использовать слово
WORD подобно тому, как мы использовали слово QUERY в гл. 9. Рассмотрим, как это делается, на
примере. Выберите блок для эксперимента (пусть это будет блок 50) и введите редактором в пер¬
вую строку блока фразу ”The quick brown fox”. Затем определите следующее слово:
: PARSE ( блок» — ) (Разбор)
BLK @ >R >IN @ >R (Запоминает прежние указатели)
BLK ! 0 >IN ! (Готово для разбора нового блока)
BEGIN 32 W0RD (Начало разбора)
COUNT ?DUP WHILE TYPE SPACE (Печатает, пока находит)
REPEAT DR0P R> >IN ! R> BLK ! ; (Восстанавливает указатели)
Теперь если ввести
50 PARSE
то вы увидите на экране
The (Выделенные
quick при
brown разборе
fox слова)
Вместо того чтобы разделять слова во входном буфере с помощью слова PARSIT из гл. 9, вы мо¬
жете разделять их непосредственно в блочном буфере словом PARSE, присваивая соответствующие
значения переменным BLK и >IN. Описанный прием извлечения строк из блоков является довольно <
эффективным для работы как со строковыми, так и с числовыми данными, запоминаемыми в форме
кодов ASCII. В этом вы убедитесь, сделав несколько упражнений.
l4l
Упражнения
1. Определите слово BLOCKWORD, которое должно выделять только одно слово из блока, запоминать его в счетной стро¬
ке с адресом PAD и выдавать в стек адрес PAD. В стеке должен находиться номер блока, смещение начального байта в бло¬
ке и код разделителя. Таким образом, если вы введете
50 8 32 BL0CKW0RD COUNT TYPE
то в PAD будет занесена счетная строка, начинающаяся с восьмого байта и завершающаяся пробелом. После этого выделен¬
ное слово будет выведено на экран.
2. Определите переменную под именем POSITION для задания смещение байта, с которого должно начинаться разделе¬
ние слов. После этого дайте новое определение слова BLOCKWORD под именем BWORD, которое должно производить раз¬
бор слов, начиная с позиции, записанной в переменную POSITION. Таким образом, если ввести
8 POSITION ' 50 32 BLOCKWORD COUNT TYPE
то действие должно быть таким же, как в упражнении 1. Кроме того, новое определение BLOCKWORD должно изменять
значение переменной POSITION так, чтобы оно указывало на следующую после разделителя позицию выделяемого слова.
Таким образом, если несколько раз подряд исполнить
50 32 BLOCKWORD COUNT TYPE
то на экран будут выведены все слова из блока.
3. Опишите слово BLOCKNUMBER, которое должно брать число из блока в форме строки символов ASCII и помещать
его в стек. Для решения этой задачи вам, возможно, придется освежить в памяти слова CONVERT и NUMBER из гл. 9.
4. Кроме извлечения строк из блоков может оказаться полезным также запоминание их в блоках. Определите слово
TOBLOCK, которое действует как BLOCKWORD, но запоминает строку по указанному адресу в указанном блоке в позиции,
определенной в переменной POSITION. Строка должна быть представлена в счетной форме, но запоминаться она должна
как строка без байта-счетчика с разделителем в конце строки. Слово POSITION нужно изменить так, чтобы оно указывало
на позицию, следующую сразу после разделителя. Таким образом, если в POSITION записан 0 и строка ”fox” записана, на¬
чиная с PAD, то при вводе
PAD 50 32 T0BL0CK
строка "fox” будет записана в блок номер 50, начиная с нулевого байта; переменная PAD будет изменена на 4, чтобы указы¬
вать на байт, следующий сразу после пробела, которым заканчивается строка ”fox”. Когда подобное изменение POSITION
может оказаться полезным?
Использование нескольких блоков в качестве файла
Если вы пользовались файлами, работая с другими языками программирования, то вам, безуслов¬
но, понятно, что в Форте мы рассматриваем отдельный блок как короткий файл. Когда мы обсужда¬
ли передачу данных между блоками и массивами чисел и символьных строк, используя оператор
CMOVE, мы показали вам так называемый метод произвольного доступа при вводе-выводе (т.е.
при вводе и выводе) данных, который применяется для доступа к файлам. Мы имели в виду, что
данные, находящиеся в записях (в нашем случае в массивах) запоминаются и извлекаются в блоке
с указанного пользователем места. В противоположность большинству языков программирования,
которые работают с записями фиксированной длины, т.е. производят обмен данными с диском фик¬
сированными порциями информации в байтах, мы могли сами выбирать любое число байтов для об¬
мена с буферами и, следовательно, с диском. Применяя терминологию других языков программиро¬
вания, это можно назвать произвольным доступом с произвольной длиной записи. Считается, что
осуществление произвольного доступа с переменной длиной записи очень прогрессивно, но пред¬
ставляет собой трудную задачу. В Форте же она решается очень просто.
Метод доступа к данным, который мы применяли в последней серии упражнений, называется
применительно к файлам последовательным доступом. Можете думать, что такое название объяс¬
няется тем, что текст последовательно считывается из файла (или блока) с помощью указателя (в
нашем случае POSITION), который указывает на положение следующей строки, которая должна
быть извлечена.
Большинство языков программирования при осуществлении последовательного доступа допускают
использование очень небольшого оговоренного заранее набора разделителей. В Форте разделителем
может быть любой символ. Так в упражнениях мы использовали для этой цели пробел. Кроме того,
в других языках программирования последовательный доступ должен всегда производиться с начала
файла, а в блоке Форта вы можете начать с любого места* Таким образом Форт позволяет очень
гибко использовать отдельный блок в качестве короткого файла. Однако очевидным недостатком
Форта является то, что его ’’файлы” не могут быть длиннее 1024 байтов. Для многих применений
142
этого объема недостаточно. Поэтому теперь мы обсудим, каким образом можно осуществить доступ
к двоичным числам и строковым данным, находящимся в настоящих файлах, состоящих из несколь¬
ких блоков. Мы можем определить новые слова Форта, которые будут подобны тем, что мы ввели в
упражнениях, за исключением одного: когда будут исчерпаны данные одного блока, они будут обра¬
щаться за продолжением к другому блоку. Но это не очень просто, поскольку непросто рабютать со
строками или числами, размещенными даже на двух блоках. Кроме того, возникает необходимость
в директории блоков, чтобы знать, в каком порядке они располагаются в файле. Тем не менее мы
можем создать так называемый файл в памяти, в который одновременно будут помещаться все
блоки и где будут выполняться все действия с ними, а запись на диск будет производиться по за¬
вершении всех операций с данными. В дальнейшем изложении мы будем рассматривать именно та¬
кие файлы в памяти. Сейчас же мы обсудим, как можно использовать их для осуществления проце¬
дуры последовательного доступа при обращении как с числами, так и со строковыми данными.
(Большую часть работы вам предстоит выполнить в упражнениях.)
Предположим, что имеется метеорологическая база данных, в которой хранятся записи о темпе¬
ратуре, относительной влажности воздуха, скорости ветра и атмосферном давлении, которые произ¬
водятся через каждые полчаса. База данных была собрана в блоки при помощи системы сбора дан¬
ных, реализованной на Форте. Одновременно необходимо работать с данньгми, накопленными за
четыре недели. Каждая запись содержит результаты четырех измерений, т.е. каждая запись состоит
из 8 байтов. За день производится 48 записей (по одной записи за полчаса), поэтому число записей
за неделю будет 48x7=336 или 336x4=1344 записей в месяц. Следовательно, для хранения записей
за месяц потребуется 1344x8=10.572 байта, которые можно разместить на 11 блоках диска. Допу¬
стим, что нужно выводить данные за день, неделю и за месяц. Можно работать с этой информацией
с помощью программы, которая обращается к очень большому массиву, размещенному в памяти,
который мы назвали файлом в памяти. Сначала мы создадим массив, в котором будут храниться
общее число блоков и их номера, например:
CREATE DATA 11 , 50 , 51 , 52 , 53 , 60 ,
61 , 62 , 63 , 64 , 65 , 66 ,
(Заметьте, что блоки могут идти не обязательно подряд.) Теперь резервируем место для разме¬
щения файла
CREATE METFILE DATA @ 1024 * ALL0T
Определим слово, которое должно перемещать данные из блоков на диске в файл METFILE:
: GETFILE ( — ) (Взять_файл)
DATA @ 1+ 1 DO (Проходит по блокам)
DATA I 2 * + @ BL0CK (Получает адрес
каждого блока)
METFILE I 1- 1024 * + 1024 CM0VE (Перемещает данные в файл)
L00P ;
При исполнении слова GETFILE файл в памяти (метафайл) будет заполнен информацией из со¬
ответствующих блоков.
.Можно дать более общее определение слова GETFILE, которое не только будет заполнять мета¬
файл, но и создавать его:
: GETFILE ( адр — )
CREATE HERE 0VER @ 1024 * ALLOT SWAP
DUP @ 1+ 1 D0
2DUP I 2* + & BLOCK SWAP I 1-
1024 * + 1024 CM0VE
L00P DR0P DR0P ;
В таком виде слово GETFILE можно использовать для создания файла из любого набора блоков,
номера которых хранятся в массиве DATA. Если массив номеров блоков устроен так же, как
DATA, то слово GETFILE используется в такой форме:
DATA GETFILE METFILE
Теперь нужно сконструировать слова для обеспечения доступа к данным в метафайле. Вспомним,
что в каждой записи имеется по четыре элемента (в 8 байтах). Поэтому запись номер 1 начинается
с 0-го байта файла, запись номер 2 — с 8-го, запись номер 3 — с 16-го и т.д. Если нам нужен тре¬
тий элемент данных из записи номер 200, то мы можем извлечь его из метафайла с помощью такой
последовательности действий:
METFILE 200 1- 8 * + 3 1- 2* &
143
Можно будет упростить эту операцию, если определить еще несколько слов. Причем можно сде¬
лать эти определения достаточно общими, чтобы пользоваться ими впоследствии для любого мета¬
файла с любой длиной записей. Сначала определим переменную для хранения длины записи:
VARIABLE RECLEN 8 RECLEN !
Затем определим переменную, указывающую на позицию начала записи, которая будет исполь¬
зоваться в такой форме:
200 METFILE RECL0C
чтобы поместить в стек адрес начала 200-й записи в файле. Вот это определение:
: RECL0C ( n адр -- адр ) SWAP 1- RECLEN @ * + ;
Теперь опишем слово DATALOC для вычисления адреса любого элемента заданной записи, кото¬
рое, например, в случае
3 200 METFILE DATAL0C
будет возвращать в стек адрес начала 3-го элемента данных в записи номер 200. Вот определение
этого слова:
: DATAL0C (n1 n2 адр1 — адр2) RECL0C SWAP 1- 2* + ;
С помощью этих общих слов мы можем определить слова для извлечения конкретных данных из
нашего файла, т.е. для извлечения, например, данных о температуре, влажности воздуха и т.д. Мы
выпишем их в порледовательности, в которой они записаны в метафайле:
ТЕМР 1 SWAP METFILE DATAL0C ; (Температура)
HUMID 2 SWAP METFILE DATAL0C ; (Влажность)
WIND 3 SWAP METFILE DATAL0C ; (Скор, ветра)
BAR0M 4 SWAP METFILE DATAL0C ; (Давление)
Тогда если вы введете
200 WIND @
то получите скорость ветра в 200-й записи. (Как вы узнаете в гл. 11, эти четыре слова можно опре¬
делить еще лучше, если применить специальное слово-определитель в конструкции
CREATE...DOES>.) Наконец, было бы неплохо определить слово, позволяющее по значениям номе¬
ра дня относительно начала текущей серии данных, по значениям часа и минуты возвращать в стек
номер записи, относящейся к этой дате. Это может быть сделано с помощью слова
: TIME->REC 30 / SWAP 2 * + SWAP 48 * + ;
Напомним, что данные собираются через каждые полчаса. Тогда, если ввести
7 10 30 TIME->REC TEMP @
то вы получите значение температуры в седьмой день от начала записей в 10 ч 30 мин. Заметьте,
что слова GETFILE, RECLEN, RECLOC и DATALOC вместе со словом PUTFILE, которые вы опре¬
делите в порядке упражнения, представляют собой простые, но весьма полезные и достаточно об¬
щие расширения языка для работы с файлами. Единственное ограничение на длину файла наклады¬
вает доступный объем памяти. У вас может возникнуть желание собрать эти слова вместе и офор¬
мить для применения в более общем виде (и еще некоторые слова, которые мы определим в следу¬
ющем разделе). Кроме того, если вам часто приходится работать с файлами данных, вы захотите
усовершенствовать обращение с файлами, чтобы добавить более мощные возможности. Мы предла¬
гаем вам еще поработать с метеорологической базой данных в следующих упражнениях.
Упражнения
1. Определите слово DATA-AVE, которое должно выдавать среднее значение данных, если задать номер первой и послед¬
ней записи и время этой записи. Так, например, если ввести
8 0 0 TIME->REC 8 23 30 TIME >REC 3 DATA-AVE
то вы получите среднее значение температуры за восьмой день, считая со дня создания файла. Здесь потребуются числа
двойной длины.
2. Определите слово TEMP-AVE, которое действовало бы как 1 DATA-AVE, т.е. чтобы не нужно было указывать положе¬
ние элемента данных в записи. Используйте в определении TEMP@.
3. Предположим, что при измерении температуры была допущена ошибка в 1 градус из-за неверной калибровки термо¬
метра, в результате чего нужно добавить к каждому значению температуры в METFILE 1 градус. Определите слово
CORRECT-TEMP, которое должно произвести указанную коррекцию.
4. Определите слово PUTFILE для копирования файла с изменениями в исходные блоки, из которых ой был загружен в
память, т.е. если ввести
METFILE DATA PUTFILE
144
то файл с измененными данными будет возвращаться на диск.
5. Определите слово SAVEREC, которое будет заменять запись, помещая в нее число из стека. Предусмотрите уход из
программы, если количество чисел в стеке меньше, чем значение переменной RECLEN.
Файлы строковых данных с последовательным доступом
В примере, который был очень подробно разобран в предыдущем разделе, мы имели дело с фай¬
лом числовых О^воичных) данных с последовательным доступом и с фиксированной длиной записи.
Если вы вспомните, что еще раньше мы пользовались словом WORD для выделения строк из блока,
то вам нетрудно будет представить, что мы можем работать также и с файлами, содержащими по¬
следовательности строк различной длины, отделенных друг от друга разделителями.
Мы рассмотрим здесь в общем виде строковые файлы в памяти, при этом вы определите в упраж¬
нениях некоторые полезные слова, а затем построим файл адресов и обсудим, как его можно ис¬
пользовать. Строковый файл (или файл строковых данных) можно описать в виде массива точно
так же, как файл метеорологических данных. Предположим, что у вас имеется 10 блоков, содержа¬
щих имена и адреса людей. Можно создать масСив, который описывает файл, следующим образом:
CREATE ADDRESS 10 , 50 , 51 , 52 , 53 , 54 ,
60 , 61 , 62 , 63 , 64 ,
Пользуясь этим массивом, можно инициализировать файл в памяти, применяя операцию
GETFILE точно так же, как мы создавали в предыдущем разделе файл METFILE. т.е. чтобы сде¬
лать это, нужно написать
ADDRESS GETFILE ADDFILE
Однако вместо того, чтобы описывать длину каждой записи в переменной, мы используем пере¬
менную для хранения байта-разделителя. Выберем сначала в качестве символа разделителя пробел,
потому что это нам потребуется в упражнениях:
VARIABLE DELIMITER 32 DELIMITER •
Теперь предположим, что нам нужно произвести разделение текста первых 64 символов в файле
на строки. Мы можем определить для этого слово PARSELINE по аналогии с PARSIT из материала
гл. 9:
: PARSELINE ( адр — ) (Разделить_строку)
BLK @ >R >IN @ >R (Запоминает указатели при вводе из блока)
TIB 80 0 FILL (Заполняет буфер ввода нулями)
TIB 64 CM0VE (Вводит в буфер 64 символа)
0 BLK ! 0 >IN ! CR (Указывает на начало буфера ввода)
BEGIN DELIMITER @ W0RD (Начинает разделение)
COUNT 7DUP WHILE TYPE SPACE (Выводит, если есть что)
REPEAT (Повторяет)
TIB 80 BLANK (Заполняет буфер ввода пробелами)
R> >IN ! R> BLK ! ; (Восстанавливает указатели блока)
Если ввести
ADDFILE PARSELINE
то первые 64 символа файла будут подвергнуты разбору, т.е. разделены на слова-строки, между ко¬
торыми в качестве разделителя стоит пробел, и выведены на экран. Главные различия между слова¬
ми PARSIT и PARSELINE состоят в том, что в данном случае перед словом указывается не номер
блока, а адрес и, кроме того, вместо конкретного кода 32 мы применили более общее DELIMITER@
и для выполнения разделения использовалось пространство в буфере ввода.
Теперь вам предстоит определить некоторые слова, которые являются полными аналогами слов
из упражнений предыдущей серии (мы даже сохраним их имена), но они должны работать не с
блоками, а с файлами.
Упражнения
1. Определите слово GETWORD, которое будет выделять только одно слово-строку из файла и запоминать его в форме
счетной строки в PAD, возвращая в стек адрес PAD. В стеке должны находиться адрес файла и смещение первого байта в
файле. Тогда после ввода
ADDFILE 8 GETW0RD COUNT TYPE
145
в PAD будет перемещена из файла и запомнена счетная строка символов ASCII, начинающаяся с байта номер 8 и заканчи¬
вающаяся пробелом. После этого выделенное слово-строка должно быть выведено на экран. Для распознавания конца слова-
строки нужно использовать переменную DELIMITER.
2. Определите переменную POSITION, в которой должна быть записана позиция байта в файле, начиная с которой нуж¬
но произвести разбор текста и выделение слов-строк. Теперь определите слово FILEWORD, которое должно выполнять раз¬
бор текста в файле, начиная с места, на которое указывает переменная POSITION. Например, последовательность операто¬
ров
8 POSITION ! ADDFILE FILEWORD COUNT TYPE
должна приводить к тому же результату, как в примере из упражнения 1. Кроме того, слово FILEWORD должно изменять
значение в POSITION так, чтобы оно указывало на байт, следующий после байта-разделителя, стоящего в конце выделенно¬
го слова-строки. Таким образом, если повторно будет исполняться
ADDFILE FILEWORD COUNT TYPE
то мы увидим последовательные слбва-строки.
3. Определите слово FILENUMBER, используя FILEWORD, которое должно брать из файла число, представленное по¬
следовательностью кодов ASCII, и помещать его как число в стек. Для этого вам, может быть, потребуется вспомнить, как
работают слова CONVERT и NUMBER из гл. 9.
4. Кроме извлечения строк из файла весьма полезно иметь возможность записывать их в файлы. Определите слово
TOFILE, которое работает аналогично слову FILEWORD, но в отличие от него запоминает строку, начиная с указанного ад¬
реса в указанный файл с позиции, указанной в переменной POSITION. Строка должна быть в исходном виде в счетной
форме, а запоминаться как несчетная строка, в конце которой находится байт-разделитель. Содержимое POSITION должно
быть изменено так, чтобы оно указывало на позицию, следующую после байта-разделителя. Так, если в POSITION записан
0 и текст "fox" помещен, начиная с адреса PAD, то при исполнении
PAD ADDFILE TOFILE
текст "fox" будет запомнен в файле, начиная с байта номер 0; содержимое переменной POSITION будет изменено на 4, т.е.
будет указывать на байт, следующий после пробела, замыкающего текст ”fox”.
Файл адресов
В гл. 9 мы построили телефонный справочник, а в данной главе мы его модифицировали, для то¬
го чтобы запоминать в блоках диска. Несмотря на свою гибкость и быстродействие, этот справочник
не лишен недостатка, состоящего в том, что для каждой записи требуется 30 байтов для имени або¬
нента и 30 байтов для адреса. Это приводит к напрасному расходованию памяти и не позволяет за¬
писывать более длинные имена. Более гибким оказывается другой подход: будем отделять вводимые
данные друг от друга с помощью байта-разделителя, в этом случае они будут занимать ровно столь¬
ко места, сколько действительно требуется. Именно так мы построим наш файл адресов.
Будем считать, что каждая запись в файле должна состоять из имени абонента, его адреса и но¬
мера телефона. Каждая запись будет разделяться на четыре поля: поле имени, поле названия ули¬
цы, поле названия города, почтового индекса и страны, поле номера телефона. Для отделения по¬
лей используем знак-разделитель ”л”(код ASCII 94). Мы ставим перед собой задачу определить сло¬
ва для вывода имени и адреса, поиска по имени, добавления новых имен и адресов в конец файла.
После этого мы предлагаем вам дополнить список слов еще некоторыми функциями в упражнениях.
Для иллюстрации структуры файла покажем пример того, как он должен выглядеть, если запро¬
сить листинг первого блока:
1 John Simpson"2223 Second St.~Louisville, РА 23456~225-294-678~
2 Jean Baptist de Lamarc~23a Rue des Arbres~Paris, France~02-955
3 6~John Trent~Cat’s House Cottage~Burton-Underhill PD56-5BC Dors
4 set England~01-35-5624~James Mathieson~238 Parkway Drive~Philade
(Помните, что файл находится в памяти, а для того, чтобы его поместить туда, нужно использо¬
вать слово GETFILE.) Для обеспечения доступа к файлу адресов, выделения полей с помощью раз¬
делителей мы воспользуемся идеями предыдущего раздела. Итак, в переменную DELIMITER нужно
записать код 94, который будет помечать конец каждого поля. Теперь пусть POSITION указывает
на начало имени. Проще всего вывести имя и адрес; для этого можно воспользоваться словом
FILEWORD из упражнения 2 предыдущего раздела. Слово
: PRINT-RECORD ( — ) (Напечатать_запись)
CR 4 0 DO
DUP FILEWORD COUNT TYPE CR
LOOP DROP ;
146
решает эту задачу, если использовать его в следующем контексте:
ADDFILE PRINT-RECORD
Но как найти начало поля имени? Для этого мы должны иметь возможность искать имя, поме¬
щая указатель POSITION в начало поля имени. Поэтому нам потребуется строковая переменная, в
которую мы поместим строку для поиска:
80 $VARIABLE SEARCH$
Нам нужно также иметь слово, которое возвращало бы указатель (POSITION) снова в начало
поля, предполагая, что содержимое поля хранится в PAD; это слово мы будем использовать, чтобы
после того, как поиск завершится успешно, переместить указатель в начало поля имени:
: POINTBACK PAD C@ 1+ NEGATE POSITION +! ;
Слово POINTBACK вычитает длину поля плюс разделитель из значения, хранимого в
POSITION. Нужно также распознавать, когда мы дойдем до конца файла. Мы будем делать это, ис¬
пользуя признак конца файла в последнем поле имени, в качестве которого выберем строку
”&&&”, определив ее как константу:
$C0NSTANT E0F &&&”
Далее мы воспользуемся некоторыми словами для работы со строками из версии MMSFORTH,
которые вы можете заменить собственными аналогами, если посмотрите их определения в гл. 9.)
Приведенное ниже слово будет возвращать в стек флаг истина, если в строке, адрес которой нахо¬
дится в стеке, будет встречен признак конца файла:
: ?E0F ( $адр — флаг ) EOF INSTR 0= 0= ;
(Чтобы понять, как используется слово INSTR, посмотрите пример с телефонным справочником
в гл. 9.) Теперь мы можем определить слово
SEARCH-NAME ( — ) (Искать_имя)
BEGIN ADDFILE FILEW0RD DUP DUP 7E0F
IF POINTBACK CR ." Имя не найдено" ABORT THEN
SEARCH$ INSTR DUP
IF POINTBACK CR SWAP COUNT TYPE ."найдено" CR AB0RT
THEN
UNTIL ;
Вот как будет использоваться слово SEARCH-NAME. Предположим, что в файле находится имя
”John Jones”, а после него ”Gary Jones”. Вы хотите найти адрес и телефон Gary, но вам нужно
вспомнить, как его фамилия.
Введите
$" Jones" SEARCH$ $!
и
0 POSITION !
а после этого
SEARCH-NAME
Вы увидите, что на экран будет выведено ”John Jones найдено”, но вы искали не это. Введите те¬
перь
PAD C@ POSITION + '
чтобы пропустить John (если желаете, определите для этой цели специальное слово NEXTFIELD),
а затем снова введите SEARCH-NAME, тогда вы увидите сообщение ”Gary Jones найдено”. Тексто¬
вая строка ”Gary Jones” была запомнена в PAD в виде счетной строки, переменная POSITION ука¬
зывала на начало поля имени. Теперь, если вы введете PRINT-RECORD, на экране вы увидите имя
абонента и его адрес. Обратите внимание на то, что, поскольку поиск производится во всех полях,
будет найдена также строка ”101 Jones St.”
Теперь нам нужно сконструировать слово для добавления строки к концу файла, т.е. перед нача¬
лом признака конца файла):
: FINDE0F ( адр — )
BEGIN DUP FILEW0RD 7E0F
UNTIL DROP POINTBACK ;
Форма использования этого слова такая:
ADDFILE FINDE0F
Кроме того, нам нужно слово, которое должно добавлять новое поле в файле, если задан адрес
счетной строки, которая должна помещаться в это поле:
: PUTFIELD ( $адр — ) $” ~" $+ T0FILE ;
147
(Слово TOFILE было определено в упражнении 4 предыдущего раздела.) Вот, например, слово
для добавления нового поля в запись адреса:
: ADD-ADDR ( — )
ADDFILE FINDEOF
Имя
’ IN$
PUTFIELD
CR
Адрес
’ IN$
PUTFIELD
CR
Город/Штат ’
’ IN$
PUTFIELD
CR
Телефон
* IN$
PUTFIELD
CR
&&&”
PUTFIELD
f
Обратите внимание на то, что новое поле наложилось на старый признак конца файла.
Несмотря на то, что приведенный пример имеет узкоспециализированное применение, он демон¬
стрирует большое число приемов работы с текстовыми файлами. Вы можете развивать его дальше в
последующих упражнениях.
Упражнения
1.0пределите слово NEXTFIELD, которое должно помещать указатель POSITION на начало следующего поля. Это зна¬
чит, что если POSITION указывает на поле имени, то NEXTFIELD должно помещать его на поле названия улицы.
2. Определите слово NEXTREC, которое, если указатель показывает на поле имени, будет помещать его на начало следу¬
ющего поля имени.
3. Определите слово FINDPHONE, которое, если в PAD задано имя абонента, будет находить его в файле и печатать но¬
мер телефона.
4. Определите слово DELREC, предназначенное для удаления записи из файла и смещения оставшихся записей так, что¬
бы перекрыть удаленную запись. Переменная POSITION должна указывать на начало поля имени удаляемой записи.
Выводы
В этой главе мы обсудили множество вопросов, начав с простейших операций вывода листинга и
загрузки программы, а закончив разбором текстовых файлов с переменной длиной записей. Мы
очень подробно рассмотрели реализацию загрузки блоков, использование блоков в качестве файлов
и файлов, организованных в памяти. Наш обзор затронул также примеры использования директо¬
рий файлов для организации их ввода в память блок за блоком. Причиной столь подробного изло¬
жения является то, что в большинстве версий Форта не предусмотрены организация блоков в фай¬
лы и хранение данных на диске. Даже если вам все это и не потребуется, мы надеемся, что вы име¬
ли возможность убедиться в возможностях Форта расширяться для решения задач, которые понача¬
лу могут показаться не присущими языку. Если вам нужны средства для работы с базами данных,
мы надеемся, что эта глава поможет вам решать задачи такого рода.
Глава 11
СОЗДАНИЕ СЛОВ-ОПРЕДЕЛИТЕЛЕЙ
Замечательная способность языка Форт к расширению является следствием наличия в нем так называемых определяющих
слов. Единственное назначение этих слов состоит в компиляции (определении или создании) других слов. Наиболее важным
из определяющих слов (слов-описателей) является : (двоеточие). Из других слов, с которыми вы к настоящему времени по¬
знакомились, это CREATE, VARIABLE, CONSTANT и т. п. Во время исполнения определяющего слова оно создает новое
слово, помещая в словарь заголовок создаваемого слова, после которого следует все остальное, что необходимо для исполне¬
ния нового слова. В заголовке содержатся имя слова и некоторая дополнительная информация. На любом этапе программи¬
рования на Форте определяющие слова используются для соединения между собой простых программ в более сложные.
Исключительной особенностью Форта, которую мы рассмотрим в этой главе, является то, что вы сами можете создать новые
определяющие слова, не ограничиваясь теми, которые предусмотрены в базовом языке Форт. И при этом создание новых оп¬
ределяющих слов производится так же просто, как и создание "обычных" слов языка. Это открывает неограниченные воз¬
можности для создания новых типов слов и новых типов данных, которые могут сделать ваши программы более эффективны¬
ми и в то же время облегчить их написание.
Порождающие и порождаемые слова
Каждое определяющее слово, входящее в ядро языка Форт, способно породить определенный
класс слов. Например, хотя каждое слово, которое определяется с помощью : (слова-двоеточия),
выполняет отличные от других слов действия, но они сходны между собой по способу определения,
компиляции и исполнения. Все слова, определенные через : Двоеточие), принадлежат к одному
классу, поскольку они составляются из более простых слов для объединения функций этих слов.
Аналогично все слова, создаваемые с помощью слова-определителя CONSTANT, относятся к классу
’’констант”, потому что все они одинаково компилируются и исполняются. Следовательно, каждое
слово можно отнести к какому-либо классу в соответствии с порождающим его словом.
Отношение между словами можно сделать более ясным, если назвать определяющие слова слова-
ми-родителями, а порожденные ими слова словами-детьми. Все слова-дети общего слова-родителя
ведут себя сходным образом, но все же отлично от других слов-детей, родных им ”по крови”. Об¬
щее в поведении всех ’’единокровных” слов-детей является следствием того, что они происходят от
одного ’’родителя”. Различия между ’’детьми” определяются тем, что при создании в них были
скомпилированы различные значения. Чтобы донять, почему слова-дети ведут себя так или иначе,
вы должны проанализировать определение слов-родителей и их собственные определения.
Если описать ’’генеалогию” слов, то мы сможем выделить три стадии, которые в литературе по
языку Форт называют ходом событий. Ход событий — это то, что происходит, когда:
1. гождается слово-родитель (компиляция определяющего слова).
2. Родитель активен и порождает (исполнение определяющего слова слово-ребенка и компиляция
слова-ребенка)
3. Ребенок действует самостоятельно (исполнение слова-ребенка)
Причина того, что мы выделяем три, а не четыре стадии, очень простая: когда определяющее
слово исполняется, оно компилирует слово-ребенок. Поэтому то, что кажется двумя стадиями, на
самом деле это одно действие. Общее в поведении слов-детей, происходящих от одного ’’родителя”,
предписывается им на первой стадии при определении слова-родителя. Слова, определенные с по¬
мощью, похожи по своему поведению из-за того способа, которым определено само это слово, а
одинаково они исполняются потому, что одно из действий определяющего слова — сделать так, что¬
бы ”дети” вели себя одинаково. Это легче всего проследить на примере слов-детей, происходящих
от VARIABLE и CONSTANT.
Все переменные кладут в стек адрес, по которому записано их содержимое, в то время как кон¬
станты выдают в стек свои значения. Различие их действия обусловлено способом определения слов
VARIABLE и CONSTANT. Различия в поведении слов-детей закладываются на стадии 2 при испол¬
нении определяющего слова и компиляции слова-ребенка. Слова-дети действуют по-разному, пото¬
му что они различаются по своему содержимому. Каждое слово, определенное через двоеточие, от¬
личается от других, потому что в его определении используются другие комбинации слов. Констан¬
149
ты отличаются друг от друга значениями величин, с которыми они были созданы. Поэтому мы мо¬
жем сказать, что слова-дети общего порождающего слова-родителя одинаковы, поскольку они оди¬
наково исполняются, но различны потому, что содержат различные значения величин или адресов
(помимо того, что они имеют разные имена и расположены в словаре в разных местах). Применяя
терминологию указанных трех стадий или хода событий, еще раз посмотрим на их последователь¬
ность:
Событие 1: Создается определяющее слово для компиляции слов-детей с определенным типом по¬
ведения.
Событие 2: Исполняется определяющее слово для того, чтобы создать слово-ребенок со своим со¬
держимым и поведением. •
Событие 3: Исполняется слово-ребенок в соответствии с тем, что слово-родитель на¬
учило слово-ребенка делать со своим содержимым.
Может показаться мистическим, что одно слово способно определить, как будет исполняться дру¬
гое слово, но в самом деле это совсем просто. Когда определяющее слово порождает слово-ребенка,
то кроме записи его содержимого слово-родитель записывает в него также адрес машинного кода
стадии исполнения. Код стадии исполнения — это программа на машинном языке, которая описы¬
вает, как должно вести себя слово-ребенок, т.е. что оно должно делать со своим содержимым. Так
как каждое определяющее слово записывает адрес специфического кода стадии исполнения во все
свои слова-дети, они и исполняются одинаково. (В гл.15 мы более детально рассмотрим действие
кода стадии исполнения в словах, определенных через двоеточие.)
Определяющие слова
Мы рассмотрели, как используются определяющие слова (слова-определители) для порождения
своих ’’отпрысков”, но как же создаются сами определяющие слова? С первого взгляда может пока¬
заться, что можно ответить на этот вопрос, просматривая содержимое этих слов в словаре. Но это
не так уж просто. Определение слова : в ядре языка выглядит так, как будто оно само определено
через двоеточие, но это, очевидно, абсурд. Еще более странно то, что слова, которые, казалось бы,
определены через :, находятся в словаре раньше, чем само :. Как разрешить проблему первичности
курицы и яйца?
Конечно, ядро Форта было создано без использования языка Форт (по крайней мере, в обычном
смысле), хотя оно выгладит так, как будто бы с использованием. Ядро должно было быть написано
на другом языке — либо на ассемблере, либо на языке высокого уровня. В этом все дело. Практиче¬
ски можно написать Форт-систему, используя другую Форт-систему. Создание нового Форта с ис¬
пользованием Форта производится программой, которая называется метакомпилятором, и о нем
уместно сказать несколько слов.
Метакомпиляция — это разновидность обычной компиляции Форта (т.е. процесса добавления но¬
вых слов в словарь). Но вместо того, чтобы строить словарь, начиная с адреса, на который указыва¬
ет слово HERE, ’’метасловарь” размещается в некотором другом месте памяти. В памяти создается
’’образ” всего кода, который потребуется новому Форту; этот код затем выгружается на диск таким
образом, чтобы его можно было запустить на исполнение с помощью операционной системы компь¬
ютера. Метакомпиляция может породить копию Форта или новый Форт, специализированный для
определенных задач, даже для выполнения на ЭВМ другого типа. Независимо от того, был ли Форт
создан на языке ассемблера, метакомпилятора или другом языке высокого уровня, кажущийся пара¬
докс появления в словаре слов, определенных через двоеточие, раньше определения двоеточия, объ¬
ясняется тем, что при создании самого ядра использовался совершенно другой способ, отличающий¬
ся от добавления к словарю новых слов. Теперь мы выяснили, как в Форте создаются определяю¬
щие слова и ’’обычные” слова, входящие в ядро Форт-системы. Ну а может ли пользователь создать
новые определяющие слова и как?
Ответом на этот вопрос является слово CREATE, которое может использоваться самостоятельно
или совместно с другим словом DOES>. Слово CREATE дает само по себе наиболее общий способ
определения новых слов в Форте. Как мы уже видели в гл.6, слово CREATE используется в форме
CREATE CHILD-WORD
где CHILD-WORD — это определяемое слово. Напомним вкратце: действие CREATE состоит в том,
что оно помещает в словарь заголовок для слова CHILD-WORD, а когда это слово исполняется, то
в стек кладется адрес его содержимого. Слово CREATE не резервирует никакого пространства после
150
заголовка определяемого слова; резервирование места выполняется отдельной операцией, обычно
словами C,, , (запятая) или ALLOT. Вы знаете также, что определение
: VARIABLE CREATE 0 , ;
создает VARIABLE как определяющее слово, которое может использоваться для определения произ-
вольного числа переменных, каждая из которых оказывается инициализирована нулем. Определе¬
ние слова VARIABLE является действием первой стадии. Действие второй стадии происходит, когда
мы используем слово так, как, например, в данном случае:
VARIABLE DISCOUNT
Ее можно разложить на отдельные события:
VARIABLE Начинает исполнение определяющего слова
CREATE Делает имя ’’DISCOUNT” словом в словаре
Запоминает адрес кодастадии исполнения
в DISCOUNT
0 , Компилирует в DISCOUNT два байта нулей
Действие VARIABLE на третьей стадии происходит тогда, когда исполняется слово DISCOUNT,
т.е. когда исполняется код стадии исполнения, который был записан в содержимое DISCOUNT сло¬
вом CREATE, при этом в стек помещается адрес содержимого DISCOUNT.
Любое слово, определенное через двоеточие, которое содержит как часть своего определения
слово CREATE, является новым определяющим словом. Как можно было бы определить
CONSTANT ? Казалось бы, это можно сделать следующим образом:
: BAD-CONSTANT CREATE , @ ; (Плохая_константа)
но мы сразу же замечаем, что слово BAD-CONSTANT работать не может, так как операция @ бу¬
дет совершаться на второй стадии, когда создается слово-ребенок, а не тогда, когда это слово долж¬
но исполняться. В действительности нам нужно определить слово CONSTANT так, чтобы содержи¬
мое слова-ребенка извлекалось на третьей стадии. Это достигается с помощью слова DOES> . Но
прежде чем рассматривать, как это осуществить, проделаем несколько упражнений, в которых мы
дополнительно познакомимся с применением слова CREATE .
Упражнения
1. Опишите определяющее слово 2VARIABLE, которое должно создать переменную для хранения чисел двойной длины.
Определите его таким образом, чтобы переменная инициализировалась нулем, и так, чтобы оно компилировало двойное чис¬
ло в стек, когда исполняется 2VARIABLE (последний метод используется в нескольких нестандартных версиях Форта для
всех переменных).
2. Опишите определяющее слово $CONSTANT, которое при исполнении в форме
$CONSTANT <имя> строка”
будет запоминать в словаре строку как счетную. При исполнении <имя> COUNT TYPE строка должна выводиться на экран.
3. Опишите слово-определитель RESERVE, которое должно создавать слова, для которых в словаре резервируется n бай¬
тов. Таким образом, с помощью
10 RESERVE 10BYTES
определяется слово 10BYTES, которое, в свою очередь, резервирует 10 байтов. Напишите слово RESERVE, которое должно
инициализировать все зарезервированные байты нулями.
4. Опишите слово-определитель BLOCKARRAY, которое должно запомнить число, взятое из стека, и после этого зарезер¬
вировать еще 1024 байта для содержимого блока, т.е.
213 BLOCKARRAY BLK1
должно создать слово BLKI, в которое должно быть занесено число 213, после которого должны следовать 1024 свободных
байта.
5. Теперь напишите слово GETBLOCK, которое должно заполнять содержимое блочного массива содержимым указанного
блока. Таким образом,
BLK1 GETBLOCK
должно поместить содержимое блока 213 в зарезервированное пространство в BLK1 (используйте слово nBLOCK). Напиши¬
те слово PUTBLOCK, которое должно пересылать содержимое блочного массива в блок с указанным номером. (Подсказка:
вспомните действие слов UPDATE и SAVE-BUFFERS или FLUSH .)
6. Напишите два слова B@ и B!, которые должны соответственно извлекать и запоминать числа в указанном блочном
массиве, т.е.
5 BLK1 B@
должно извлекать пятый элемент из блочного массива, который должен прийти из блока 213.
151
Эти упражнения показывают, что для хранения-извлечения и манипулирования с данными на диске могут быть полезны¬
ми специальные определяющие слова и специализированные массивы. Такого рода специализированные слова особенно по¬
лезны при создании новых версий языка Форт для разработки программ обработки данных. Например, можно определить
типы массивов для хранения данных в полях различных размеров, в частности для файлов инвентаризации, файла медицин¬
ских наблюдений и т.д. Имея специализированные типы массивов и специализированные определяющие слова, проще орга¬
низовать слежение за тем, где и в каком формате хранятся данные. Например, массив BP-BLK (блок^давления_крови) мо¬
жет содержать записи о кровянсм давлении пациентов, и если он организован по вышеописанной схеме, то вам не надо по¬
мнить, в каком блоке он записан. Эту идею можно распространить на соответствующие блоки для веса, роста и других пока¬
зателей пациентов. Возможности здесь не ограничены.
Создание новых определяющих слов
Каждый раз, когда слово CREATE используется внутри определения через двоеточие, мы создаем
новые определяющие слова. В упражнениях вы имели дело с разнообразными определяющими сло¬
вами, которые по-разному действовали при компиляции слов-детей, но все порожденные слова-дети
действовали одинаково при исполнении: они оставляли адрес своего содержимого в стеке. Слово
DOES> нужно для того, чтобы определяющее слово задало способ поведения слова-ребенка на ста¬
дии исполнения. Теперь мы можем определить константу следующим образом:
: CONSTANT CREATE , DOES> @ ,
На первой стадии деятельность слова CONSTANT проявляется во время его компиляции. Если
слово CONSTANT исполняется, например, для компиляции слова
1024 CONSTANT 1K
то на второй стадии действия слова CONSTANT можно расчленить следующим образом:
CONSTANT Начинает исполнение определяющего слова
CREATE Заносит в словарь имя "1К”
Запоминает адрес кода стадии исполнения
в слове 1K
Компилирует число 1024, взятое из стека
Из присутствия в определении DOES> @ мы узнаем, что на третьей стадии действие слова
CONSTANT (при исполнении lK) более сложное, чем в случае VARIABLE . При исполнении lK
вначале в стек кладется адрес содержимого lK (потому что слово CREATE помещает код стадии ис¬
полнения в lK, чтобы работать таким образом) и после этого @ извлекает содержимое из этого ад¬
реса, помещая в стек число 1024. Другими словами, оператор @, следующий после DOES>, испол¬
няется тогда, когда слово-ребенок исполняется, а не тогда, когда оно определяется.
Определение слова CONSTANT является отличным примером создания новых определяющих
слов. Слова, которые находятся между CREATE и DOES>, исполняются на второй стадии, т.е. тог¬
да, когда исполняется слово-родитель и компилируется слово-ребенок. Когда исполняется само сло-
во-ребенок, то вначале оно помещает в стек адрес его содержимого, а потом исполняются слова, ко¬
торые находятся в определяемом слове после DOES>, описывающие, что должно делать слово-ребе-
нок.
Приведем пример использования определяющих слов, с которыми мы вновь встретимся в гл.12 и
13, когда будем обсуждать разработку программы-редактора. Как вы уже знаете, многие терминалы
и принтеры управляются кодами ASCII со значениями 0 — 31 (их называют управляющими). Зна¬
чения управляющих кодов должны быть записаны в константах и выводиться на терминал словом
EMIT, но лучше для этого определить специальное слово
IS-C0NTR0L CREATE , D0ES> @ EMIT ; (код_управления)
IS-CONTROL — это не что иное, как CONSTANT, в которое добавлено слово EMIT, описываю¬
щее поведение слова IS-CONTROL при исполнении. Слово IS-CONTROt можно использовать для
создания целого семейства родственных слов, например:
7 IS-C0NTR0L BELL (звуковойсигнал)
8 IS-C0NTR0L BACKSPACE (возвратвлево)
12 IS-C0NTR0L FORMFEED (подача_страницы)
13 IS-C0NTR0L CR (возврат_каретки)
где каждое слово будет задавать терминалу определенное действие. Одно из достоинств определяю¬
щих слов уже очевидно: они способствуют улучшению читабельности программ. Например, при ис¬
пользовании слова IS-CONTROL нужно только конкретизировать данные, которыми отличается но¬
вое слово от других слов-детей, порождаемых словом IS-CONTROL, а именно управляющим кодом
152
и именем. Каждое слово-ребенок в семействе определений имеет свою индивидуальность. Определя¬
ющие слова дают вам возможность разграничить общее поведение слов, имеющих общее происхож¬
дение, и их индивидуальное поведение. Общее поведение слов-детей запрограммировано в исполни¬
тельной части определения слова, следующей после DOES>. Индивидуальное поведение слов-детей
определяется значением (или значениями), крторое находилось в стеке, когда оно создавалось, и,
конечно, его уникальным именем.
Задачи, для которых требуется некоторое количество слов, имеющих сходные определения, луч¬
ше всего решаются при помощи нового определяющего слова. Вот еще один пример. В гл.4 мы по¬
казали вам способ представления математических функций на Форте. С помощью определяющего
слова можно создать любое количество линейных уравнений вида
у = ax + b
путем создания слов-детей с коэффициентами а и b, находящимися в стеке. Если затем будет ис¬
полняться слово-ребенок и в стеке находится значение x, то оно будет оставлять значение решения
— у. Приведем определение этого определяющего слова:
: LINEAR ( а b —) (линейная_функция)
CREATE SWAP , ,
. DOES> DUP >R @ * R> 2+ @ + ;
Обратите внимание, что величины а и b переставляются в стеке при создании слова LINEAR,
чтобы избавиться от стековых манипуляций на стадии исполнения. Если мы определили линейное
уравнение y=3x+17 при помощи
3 17 LINEAR ALINE
то, когда оно будет исполняться в форме
2 ALINE .
мы увидим решение 23. Исполнение слова ALINE можно описать следующим образом:
DUP
( — 2 адр адр)
Адрес числа 3 in ALINE
>R
( — 2 адр)
Помещает адрес в стек возвратов
@
( — 2 3)
Извлекает число 3 (а)
*
( — 6)
6 = ах, первый член
R>
( — 6 адр)
Адрес числа 3 в ALINE
2+
( — 6 адр+2)
Адрес числа 17 в ALINE
@
( — 6 17)
Извлекает 17 (b), второй член
+
( — 23)
23 = у = ах + b, решение
Пример показывает общую методику, используемую в сложных определяющих словах. Так как
адрес первого элемента ALINE потребуется для извлечения двух чисел, его запоминают в стеке
возвратов. После извлечения адреса из стека возвратов его нужно увеличить на 2, чтобы указать на
следующий элемент ALINE, при этом в него будет скомпилировано число 17, которое надо извлечь.
Хотя в этом примере мы обошлись обычными словами для стековых манипуляций, в случае, если в
словах-детях нужно запомнить несколько чисел или байтов, могут потребоваться некоторые измене¬
ния технических приемов.
Лучше всего можно оценить мощь определяющих слов из практических примеров. Из приведен¬
ных ниже упражнений вы сможете извлечь еще некоторые идеи.
Упражнения
1. Определите слово 2CONSTANT, которое должно работать так же, как и CONSTANT, но с двойными числами.
2. Определите слово MAKEDATE (создать_дату), для которого в стеке должны находиться числа: месяц, день и год, что¬
бы оно при исполнении выдавало дату с косой чертой в качестве разделителя. Например,
12 7 41 MAKEDATE PEARLHARBOR
должно создать слово PEARLHARBOR, которое при исполнении должно выдавать дату в виде 12/07/41. (Вспомните вывод
по шаблону из гл.5.)
3. Определите определяющее слово COUNTER (счетчик), которое использует число из стека, чтобы инициализировать
порождаемые им слова. Когда исполняются слова-дети COUNTER, то при очередном исполнении их содержимое должно из¬
меняться таким образом, что
0 COUNTER COUNTIT
должно создать COUNTIT, которое при исполнении будет последовательно изменять свое содержимое: 1, 2, 3 и т.д. Как
можно извлечь содержимое COUNTIT ?
153
4. Как можно использовать слова, производные от COUNTER, для подсчета частоты использования определенных слов в
программе?
5. Для регистрации цвета фотографических красителей экспериментально определяют насыщенность составляющих цве¬
тов : голубого, желтого и пурпурного, значения каждой из которых могут изменяться в диапазоне от 0 до 255. Определите
слово COLOR, которое берет из стека величины насыщенности цветов, а порождаемые им слова должны выдавать насыщен¬
ность каждого из трех цветов в процентах. Так, например, 128 128 128 COLOR lDYE будет создавать слово lDYE, которое
при исполнении должно вывести
”50% голубого, 50% желтого, 50% пурпурного". Возможно, что прежде, чем определить само слово COLOR, вы захотите
определить три слова, которые будут выводить процентное содержание цвета.
6. Определите слово QUADRATIC, которое работает подобно LINEAR, но определяет порождаемые им слова, выдающие
в стек решение уравнения у - ax^ + bx + с, если вначале в стеке находится x.
7. Уравнение Михаэлиса-Ментена широко применяется в биологии и биохимии для определения скорости энзиматических
реакций. Его общий вид такой:
Q = QMaKcS / (км + S)*
где Q — скорость реакции, S — концентрация субстрата, QMaKC — максимальная скорость реакции и КМ — константа по-
лунасыщения, т.е. концентрация субстрата, при которой скорость реакции составляет половину от максимального значения.
Напишите определяющие слова для решения этого уравнения, причем в фазе компиляции в стеке находятся значения QMaKC
и KM, а значение S задается в стеке во время исполнения.
8. Многочлен n-й степени является очень полезной функцией ввиду того, что он может "имитировать" почти любую дру¬
гую функцию. Многочлен имеет вид
у = a^ + a2X +agX^ + . . + апх^п“^.
Напишите порождающее слово POLYNOM, для которого в стеке заданы некоторое количество коэффициентов (т.е. зна¬
чений aj), и производные от него слова-дети будут рассчитывать значение полинома (у), используя эти коэффициенты. Вам
потребуется использовать в определяющем слове циклы DO-LOOP после CREATE и после DOES>. Понятно ли вы вам, как
POLYNOM может заменить слова QUADRATIC и LINEAR ?
9. Определите слово <CONSTANT>, которое должно использоваться в форме
нин накс n <C0NSTANT> A-CONSTANT
где A-CONSTANT будет иметь начальное значение n, но возвращать число мин, если его содержимое меньше, чем мин, или
число макс, если его содержимое больше, чем макс. (Содержимое A-CONSTANT должно быть изменено при помощи * или ’
>BODY и ! .) '
Определение массивов
В гл.6 мы ознакомились с созданием одномерных массивов с помощью слов CREATE и ALLOT,
однако, чтобы извлекать или записывать данные, нам приходилось вычислять величину смещения
адреса. Слова-определители находят превосходное применение при создании массивов, поскольку с
их помощью можно создать описание массива с определенной размерностью, которое при исполне¬
нии будет выдавать адрес нужного элемента в стек. Приведем пример слова-определителя для со¬
здания массива из символов, или байтов, с именем CARRAY. Один из возможных вариантов слова-
определителя такой:
: CARRAY CREATE ALLOT D0ES> + ;
Если его использовать в форме
30 CARRAY NOVEMBER
то будет создан массив из 30 байтов, элементы которого нумеруются числами от 0 до 29. На стадии
исполнения слова-ребенка требуется наличие в стеке номера элемента массива, чтобы вычислить
его адрес. Таким образом, при исполнении
1 NOVEMBER C@ .
будет рассчитан адрес второго байта в массиве NOVEMBER, извлечено его содержимое и после это¬
го выведено на экран.
Существуют два способа нумерации элементов массивов, начиная либо с нулевого элемента (как
в вышеприведенном случае), либо с элемента 1. Если вы хотите, чтобы выражение lNOVEMBER
выдало адрес первого (а не второго) байта в массиве NOVEMBER, определите CARRAY таким об¬
разом:
: CARRAY CREATE ALLOT D0ES> 1- + ;
Несмотря на то, что нумерация с первого элемента приводит к небольшому проигрышу в скоро¬
сти, вы убедитесь в том, что она удобнее для работы. Для создания массивов чисел одинарной дли-
154
ны можно использовать похожее определение (принимая также, что нумерация элементов начина¬
ется с нуля) :
. ARRAY CREATE 2 * ALLOT DOES> SWAP 2 * + ;
Для регистрации цвета фотографических красителей уверены, что вы сможете самостоятельно
проанализировать, что делает это определение. Можете ли вы предложить определение, которое со¬
здает массив, начинающийся с первого элемента ?
В упражнениях мы предложим вам написать эквивалентное слово для создания массивов двойных
чисел. Обратите внимание на то, что массивы, порождаемые этими простыми определениями, будут
безропотно возвращать в стек адреса элементов, находящихся вне резервированного адресного про¬
странства, если номер элемента выходит из заданного диапазона. Запись данных за пределами ре¬
зервированной области может привести к катастрофическим последствиям. Поэтому корректное ре¬
шение задачи состоит в том, чтобы написать слово, которое производит проверку значения номера
элемента массива, прежде, чем его использовать для работы с определенным массивом, на допусти¬
мость, т.е. попадание его в область разрешенных значений. Однако включение проверки в опреде¬
ление массивов ARRAY и CARRAY приведет к снижению быстродействия, независимо от того, бу¬
дет обнаружена ошибка или нет. Если скорость не очень важна, то можно переписать определения
массивов, включив в них проверку ошибок. В таком случае в слово, определяющее массив, наряду с
размером резервированной для массива области должно быть скомпилировано число элементов
(чтобы использовать для проверки). Если принять, что номер младшего элемента массива равен 0,
то одним из возможных определений, выполняющих проверку, может быть
• ECARRAY ( n —) CREATE DUP , 2+ ALLOT
DOES> DUP @ 3 PICK SWAP U< (2 PICK для Форт-83)
IF + 2+ ELSE .’’ Ошибка индекса” ABORT THEN ;
Приведенное определение можно расчленить на отдельные действия следующим образом:
: ECARRAY (Имя определяющего слова)
CREATE (Создает заголовок слова-ребенка)
DUP (Копирует число элементов массива в стеке)
(Помещает в слово-ребенок адрес кода
стадии исполнения)
(Компилирует максимальный номер элемента)
2+ (Устанавливает число байтов, необходимых
для компиляции массива)
ALLOT (Резервирует в словаре место для массива)
DOES> (Определяет поведение слова-ребенка при ис
полнении)
(Номер элемента находится в стеке)
(Оставляет в стеке адрес содержимого слова-ребенка)
DUP ( — n адр адр )
@ ( — n адр макс ) (Максимальная размерность массива)
3 PICK ( — n адр макс n ) (Номер элемента)
(2 PICK для Форт-83)
SWAP ( — n адр n макс )
U< (Номер элемента меньше допустимого?)
IF (Если номер меньше допустимого...)
+ (Вычисляет адрес элемента и)
2+ (смещение для обхода максимального значения)
ELSE (Но, если номер элемента слишком большой)
.” Ошибка индекса” (то выдает сообщение об ошибке)
ABORT (очищает стек и прекращает работу)
THEN
; (Заканчивает определение)
Не могли бы вы попытаться несколько повысить эффективность этого определения, используя
стек возвратов?
Слово для проверки недопустимого значения индекса массива в определении чисел одинарной
длины будет иметь такую же форму, однако в нем должна быть предусмотрена возможность ис¬
пользования для каждого числа двух байтов. Чтобы обнаружить, что индекс слишком велик или по¬
падает в область отрицательных значений, применяется оператор сравнения U<. (Понимаете ли вы,
почему?) Заметьте, что большинство слов, входящих в определение, работает независимо от нали¬
155
чия ошибки, поэтому проверка ошибок наказывает нас снижением скорости. В связи с этим следует
применять определение ECARRAY, как правило, для целей отладки, но не в отлаженной программе.
Упражнения
1. Создайте слово для определения массивов чисел двойной длины DARRAY, которое работает так же, как CARRAY и
ARRAY .
2. Перепишите определения ARRAY и EARRAY с именами lARRAY и iEARRAY, принимая номер начального элемента
равным 1. (Помните о необходимости ввести проверку ошибки в EARRAY.)
3. Напишите новые версии слов CARRAY и ARRAY, назвав их OCARRAY и OARRAY, которые инициализируют все эле¬
менты массива-ребенка нулями.
4. Напишите определяющее слово PRESERVE, которое должно скомпилировать все слова, находящиеся в массиве, в имено¬
ванный массив.
5. Модифицируйте слово, определенное в упражнении 4 (назовите его SAVE-TO-RETURN), которое при исполнении воз¬
вращало бы в стек числа в первоначальной последовательности.
6. Это полезное, хотя и не очень приятное упражнение. Создайте слово .WORD, производные слова от которого при испол¬
нении просто печатают свое имя. Таким образом, в результате исполнения
WORD Nasty
будет создано слово .Nasty, которое будет печатать на экране "Nasty”.
Отвлечение: реализация игры ’’Жизнь”
Большинство из тех, кто был связан с работой на компьютерах в 70-х гг., по крайней мере, слы¬
шали что-либо об игре ’’Жизнь”, придуманной английским математиком Джоном Конвейем. Статья
Мартина Гарднера в рубрике ’’Математические игры” в журнале Scientific American вызвала поваль¬
ное увлечение, которое привело, как говорят, к такому расточению машинного времени на ЭВМ, как
ни одна другая проблема. В наше время вследствие возросшего искусства программистов эта задача
не представляется более бросающей вызов. (Например, с MMSFORTH поставляется программа, зани¬
мающая всего 5 блоков.)
Принципы игры ’’Жизнь” очень простые: перед началом игры с помощью простого редактора на
экране дисплея изображаются колонии ’’клеточных бактерий” (представленные простыми графиче¬
скими образами или буквами). После того как введена картина их исходного расположения, начина¬
ется жизнь первого ’’поколения”. У каждой клетки имеется восемь соседних позиций (по числу ос¬
новных направлений стрелки компаса), в которых могут находиться или не находиться другие клет¬
ки. Течение ’’эволюции” расположения клеток зависит от числа соседей, которое имеет каждая клет¬
ка: если их у нее меньше двух, то говорят, что она ’’умирает” от ’’одиночества”, если больше четы¬
рех — то от ’’тесноты”. Однако клетка, имеющая двух или трех соседей выживает до следующего
поколения. Кроме того, если вокруг какой-либо незанятой позиции образуется точно три соседние
клетки, то в этом месте спонтанно возникает новая клетка нового поколения. При выполнении таких
простых правил исходное поколение кл^гок может прийти к гибели, к конечному устойчивому состо¬
янию или изменяться в течение нескольких, сотен и даже тысяч поколений. Ход игры всегда интере¬
сен, удивительно непредсказуем и приводит к очень любопытным конфигурациям клеток.
Для программирования игры ’’жизнь” йужно создать два массива. Первый массив — это массив
клеток, изображаемых на экране. Второй — массив числа соседей каждой клетки. После завершения
подсчета числа соседей значения элементов массива используются для определения мест, где клетки
умирают, продолжают жить или возникают. Эта информация используется для обновления первого
массива, который будет представлять следующее поколение. Вас может заинтересовать программиро¬
вание игры ’’Жизнь” на Форте, однако мы ограничимся здесь составлением программы только для
одномерного случая игры LINELIFE, в которой каждая клетка может иметь только двух соседей:
слева и справа. В этом случае умирает клетка, не имеющая соседей или имеющая двух соседей, но
те, которые имеют только одного соседа, будут выживать. Если соседи находятся по обе стороны от
незанятой позиции, то в ней возникает новая клетка. Задача составления этой программы для нас
состоит не столько в том, чтобы сделать интересную игру, сколько в том, чтобы рассмотреть еще
один пример программирования с использованием конструкции слов-определителей CREATE...DOES.
156
Программа начинается с объявления констант
42 CONSTANT CHAR 66 CONSTANT LLEN
где CHAR — это символ ”*”, используемый для изображения клетки, а LLEN — это увеличенная
на 2 длина строки, выводимой на экране. Слово LLEN делается на два больше длины выводимой
строки для того, чтобы программа подсчета числа соседей могла работать и с крайними позициями
строки. Следующим шагом будет резервирование места для двух массивов с помощью
CREATE IMAGE LLEN ALLOT IMAGE 1+ CONSTANT *IMAGE
CREATE CALCS LLEN ALLOT CALCS 1+ CONSTANT *CALCS
где IMAGE — это строка, которая должна быть выведена на экран, а CALCS — массив информа¬
ции о числе соседей каждой клетки. Первые позиции в IMAGE и CALCS названы соответственно
#IMAGE и #CALC. Теперь мы определим слова
. CLEAR-IMAGE IMAGE LLEN 32 FILL ; (очистка_изображения)
: CLEAR-CALCS CALCS LLEN 0 FILL ; (очистка_счетчика_соседей)
для того, чтобы производить очистку массивов, заполняя IMAGE пробелами и CALCS нулями. Для
каждой клетки, обнаруженной в массиве IMAGE, мы должны в соответствующих элементах справа
и слева от этой клетки массива CALCS увеличить их содержимое на единицу. Эта задача решается
двумя словами:
: INCS ( адр —) >R R@ C@ 1+ R@ С! (Слева от клетки)
R@ 1+ C@ 1+ R@ 1+ C' (На месте клетки)
R@ 2+ C@ 2 R> 2+ С! (Справа от клетки) ;
: INC-CALC ( n —) DUP *IMAGE + C@ CHAR =
IF *CALCS + 1- INCS ELSE DROP THEN ,
где слово INCS инкрементирует байты по трем адресам: слева, справа и в текущей позиции, а сло¬
во INC-CALC анализирует n-й символ массива IMAGE, не равен ли он CHAR (т.е. символу, изо¬
бражающему клетку), и если это так, то добавляет 1 к соответствующим элементам в массиве
CALCS . Подсчет соседей в каждой позиции для всей строки производится при помощи слова:
: CALCULATE CLEAR-CALC LLEN 0 DO I INC-CALC LOOP ;
Оно занимается тем, что рассчитывает изменяющиеся значения элементов массива CALC для
кущего расположения клеток в массиве IMAGE, затем информация, имеющаяся в массиве CALC,
должна быть переведена в новую картину, соответствующую новому поколению клеток.
Последнее выполняется процедурой
: MAKE-IMAGE CLEAR-IMAGE LLEN 0
DO *CALCS I + C@ DUP 1 = SWAP 2 = 0R
IF CHAR *IMAGE I + С! THEN
LOOP ;
которая очищает массив IMAGE, затем с помощью цикла проходит по всем позициям массива
CALCS, определяя, равно ли значение элемента 1 или 2, и если это так, то в массив IMAGE, выво¬
димый на экран, помещается клетка (т.е. символ CHAR). Это все, что касается собственно логики
программы. Остальная часть программы служит для того, чтобы загрузить ее, записать начальные
значения в массивы и установить начальные условия, т.е. инициализировать эволюцию LINELIFE .
Рассмотрим слово-определитель MAKEDO. Оно может порождать другие слова, которые будут
забирать из стека различное число параметров, компилировать их и потом использовать для иници¬
ализации начала игры LINELIFE. Каждое число, которое берет из стека MAKEDO, соответствует
позиции, куда должна быть помещена клетка в массив IMAGE перед началом игры; это дает вам
возможность связать описание любого количества исходных картин расположения клеток с именем.
Такой подход представляет собой простую альтернативу созданию специального редактора для вво¬
да картины начала эволюции
• MAKEDO CREATE DEPTH DUP С, 0 DO С, LOOP
DOES> CLEAR-IMAGE DUP ’ C@ 1+ 1
DO DUP I + C@ * IMAGE + CHAR SWAP С! LOOP DROP ;
Слово MAKEDO заслуживает более внимательного рассмотрения:
: MAKEDO (Имя слова-определителя)
CREATE (Компилирует заголовок слова-ребенка)
(Помещает в слово-ребенок адрес кода стадии
исполнения)
DEPTH (Помещает в стек значение его глубины)
DUP С, (DUP и компилирует глубину стека)
157
0 DO (Начинает просмотр стека DEPTH раз...)
(Компилирует каждый элемент из стека)
LOOP (пока они не кончатся)
DOES> (Начинает определение стадии исполнения слова-ребенка)
(Помещает в стек адрес содержимого слова-ребенка)
CLEAR-IMAGE (Перед началом очищает массив IMAGE)
DUP ( — адр адр)
C@ ( — адр глубина) (Количество чисел в стеке)
1+ ( — адр глубина+1)
1 DO (Начинает цикл извлечения скомпилированных
позиций в стек)
DUP ( — адр адр)
I (Индекс цикла, начинает извлечение с 1-й позиции)
+ ( — адр адр+i) (Адрес байта)
C@ (Извлекает элемент i-й позиции из слова-ребенка)
*IMAGE ( — адр адр+i адр) (Показывает первую клетку в IMAGE)
+ (Адрес, по которому клетка запоминается в IMAGE)
CHAR ( — адр адр_клетки симв) (симв - символ клетки)
SWAP ( — адр симв адр_клетки)
С! (Записывает клетку в массив IMAGE)
LOOP ( — адр) (Проходит по всем позициям)
DROP ( —) (После завершения удаляет ненужный адрес)
(Конец определения)
Определение слова MAKEDO позволяет скомпилировать произвольное число клеток в LINELIFE,
не указывая в явном виде, сколько их есть. Таким образом, как
1 17 32 MAKEDO 1D0
так и
2 3 4 15 16 22 33 40 MAKEDO 2D0
являются одинаково правомерными определениями. При исполнении слова, определенного с по¬
мощью MAKEDO, вначале осуществляется очистка массива IMAGE, после чего производится про¬
извольное размещение клеток в соответствии с заданным при компиляции слова-ребенка. Это пер¬
воначальное расположение клеток будет использовано для начала игры LINELIFE.
Чтобы показать текущее расположение клеток, мы используем слово
: SHOW-IMAGE *IMAGE LLEN 2 - -TRALING TYPE CR ;
Оно выводит LLEN байтов и делает возврат каретки. Теперь мы можем написать главное слово
LINELIFE, которое показывает текущее состояние массива IMAGE (которое также порождено сло¬
вом MAKEDO) и производит расчеты для неопределенного количества поколений, основываясь на
начальном размещении клеток и правилах игры. Это слово определяется следующим образом:
. LINELIFE CR BEGIN CALCULATE MAKE-IMAGE SHOW-IMAGE
0 UNTIL ;
и может быть использовано в форме lDO LINELIFE, 2DO LINELIFE и т.д. Несмотря на то, что
программа LINELIFE сравнительно простая (и не столь интересная, как настоящая программа игры
’’Жизнь”), она дает нам возможность необычного использования определяющих слов в практиче¬
ских целях. Благодаря тому, что слово MAKEDO может обращаться с любым числом позиций в
стеке при компиляции производных слов, нам удалось обойтись без редактора, необходимого для
задания первоначального расположения клеток, которое требуется для начала игры LINELIFE. Сло¬
ва, порождаемые MAKEDO, позволяют очень просто запоминать исходное состояние клеток для по¬
следующих проходов игры.
Прикладная программа на языке ФОРТ для сбора данных
Одной из наиболее важных областей применения Форта является взаимодействие с внешними ус¬
тройствами компьютера. В нашем заключительном примере мы покажем использование конструк¬
ции CREATE...DOES> для облегчения сбора и анализа данных в реальном масштабе времени с по¬
мощью компьютера. Этот пример был взят из практического применения Форта одним из авторов в
его исследовательской работе. Собственно говоря, он изучил Форт именно потому, что ни один дру¬
гой язык не позволял ему так просто добиться того, что он хотел. Задача состояла в том, чтобы
158
снять значения разнообразных показателей свойств воды в озере с помощью датчиков, ввести зна¬
чения в компьютер, а затем обработать данные, чтобы понять их изменения и взаимосвязь.
Общее число датчиков может доходить до 48, они включают в себя фотоэлементы, электроды для
измерения концентрации ионов водорода (pH), приборы для измерения прозрачности воды и изме¬
рения потоков, а также электроды для измерения растворенного в воде кислорода. Сборки с датчи¬
ками погружались в воду озера. Перед нами стояла задача иметь возможность наблюдать, что изме¬
рено в данный момент, а также результаты измерений за последние 24 ч. Датчики через кабели со¬
единялись с компьютером, который находился на берегу. Каждый прибор выдавал на выходе напря¬
жение от 0 до 1000 мВ, пропорциональное измеряемой физической величине. Выход его был соеди¬
нен с аналого-цифровым преобразователем, чтобы получить цифровую величину для ввода в компь¬
ютер через порты ввода. Для нашего примера мы будем предполагать, что написана программа на
языке Форт, и с помощью нее величины, выраженные в милливольтах, вводятся в массив PORT-
DATA так, что, например, с помощью 1 PORT-DATA @ можно положить в стек текущее значение
из порта номер 1 в милливольтах. Программу для сбора данных написать несложно, но она будет
сильно зависеть от применяемого компьютера и версии языка Форт.
Задача для нас состоит в том, чтобы преобразовать напряжение на входе в фактические значения
pH, температуры и т.д. перед тем, как в дальнейшем представить их в виде таблиц, графиков и за¬
писей на диске; при этом для каждого датчика имеется своя функция преобразования. Мы ограни¬
чимся здесь только температурными измерениями, поскольку процедуры измерения других пара¬
метров во многом похожи. Любой датчик температуры дает на выходе напряжение, которое связано
с температурой линейной пропорциональностью, т.е.
Т = aV + b,
где T — температура, V — напряжение, а и b — константы, которые определяются при калибровке
и для каждого датчика имеют свои значения. Нам нужно для каждого датчика создать слово, кото¬
рое будет брать из стека значение напряжения, рассчитывать значение температуры (на практике
умноженное на 1000) и запоминать результат в массиве RESULT. Для создания слов, например,
lTEMP, 2TEMP и т.д. можно использовать слово-определитель
port# а b V0LT-T0-TEMP nTEMP (#порта)
где nTEMP — слово-ребенок. Например, если для датчика температуры с номером 8, подключенно¬
го к порту номер 32, уравнение имеет вид: T * 26v + 1200, тогда соответствующее ему слово для
преобразования милливольт в значение температуры выглядит так:
32 26 1200 V0LT-T0-TEMP 8TEMP
а слово для преобразования напряжение-температура VOLT-TO-TEMP можно определить следую¬
щим образом:
: V0LT-T0-TEMP (Имя определяющего слова)
CREATE (Компилирует заголовок слова-ребенка)
(Помещает в слово-ребенок адрес кода
стадии исполнения)
R0T , SWAP , , (Компилирует #порта, а, b)
D0ES> (Начинает определение стадии исполнения слова-ребенка)
(Помещает в стек-адрес содержимого слова-ребенка)
>R ( — ) (Помещает адрес в стек возвратов)
R@ @ ( — #порта ) (Помещает в стек номер порта)
P0RT-DATA @ ( — данные ) (Извлекает значение напряжения из порта n)
R@ 2+ @ ( — V а ) (Извлекает а)
* ( — V*a ) (Вычисляет значение переменной компонента)
R@ 4 + @ ( — V*a b ) (Извлекает b)
+ ( — результат ) (Рассчитывает температуру)
R> @ ( — результат «порта ) (Помещает в стек номер порта)
RESULT ! ( — ) (Запоминает результат в n-й позиции массива)
; (Конец определения)
где начальные значения компилируются в слово-ребенок в такой же последовательности, в какой
они появляются в стеке. При исполнении слова-ребенка, например 8TEMP, значение берется из со¬
ответствующего порта (в данном случае 32-го), преобразуется в температуру благодаря исполнению
слов, находящихся в его определении после слова DOES>. В итоге результаты запоминаются в ра¬
нее определенном массиве RESULT. Аналогичные определяющие слова существуют для каждого ти¬
па датчика, например, рН-измерителя, датчика содержания кислорода и т.д. Они будут отличаться
154
после слова DOES>, поскольку отличаются уравнения для преобразования измеренных величин для
каждого типа датчика.
Адреса производных слов помещаются в массив для векторного исполнения, который называется
CONVERT-VECT, в порядке следования номеров портов, относящихся к датчикам портов.
Главное слово, которое помещает в массив RESULT результаты измерений, может быть опреде¬
лено довольно просто:
: CONVERT-VOLTS 48 0 DO CONVERT-VECT I 2 * + @ EXECUTE ;
Хотя мы показали вам только часть программы (сбор данных, их хранение и представление го¬
раздо сложнее), вы убедились, что слова-дети, порождаемые определяющими словами, можно легко
приспосабливать к различным типам датчиков с учетом индивидуальных калибровочных характери¬
стик. С помощью определяющих слов калибровочные параметры компилируются непосредственно в
индивидуальное слово для каждого датчика. Это пример того, что с помощью определяющих слов
программа на исходном языке становится более удобочитаемой, структурированной и компактной.
Выводы
Возможность создавать новые определяющие слова — пожалуй, единственное наиболее мощное
средство программирования на языке Форт. Если использование конструкции CREATE...DOES> ка¬
жется вам странным или даже отпугивающим, не поддавайтесь искушению обойтись без них. Луч¬
ше попытайтесь поработать над словами собственного изобретения, чтобы развить первоначальное
интуитивное представления о процессах, происходящих при определении новых слов. Любую про¬
грамму, в которой требуются повторяющиеся определения, можно сделать более изящной, если
применить слово-определитель для создания класса слов, выполняющих сходную работу. Такие
программы будет легко разрабатывать, они станут более компактными и удобочитаемыми,
В гл.16 мы обсудим программирование на Форт-ассемблере, который в двух существенных мо¬
ментах имеет прямое отношение к словам-определителям. Во-первых, Форт-ассемблер представляет
пример использования всей мощи слов-определителей. Так, ассемблер для микропроцессора 8080
описывается с помощью всего трех блоков исходного кода, при этом в него включены слова для ор¬
ганизации ветвлений и циклов, а для его реализации требуется всего пять слов-определителей, что¬
бы создать 70 слов мнемоники ассемблера. Во-вторых, если слова, порождаемые словами-определи-
телями, исполняются недопустимо медленно, то вы можете определить их действие на стадии ис¬
полнения с помощью слова ;CODE (если вы располагаете Форт-ассемблером). Это слово использу¬
ется в определениях вместо DOES>, что позволяет применить ассемблерную мнемонику для описа¬
ния действий порождаемого слова на стадии исполнения. В гл.16 мы используем слово ;CODE для
создания версии слова ARRAY на ассемблере. Применение слов-определителей может коренным об¬
разом изменить ваш стиль программирования. Через некоторое время вы будете удивляться, как вы
могли раньше обходиться без них.
Глава 12
РЕДАКТОРЫ ФОРТА
Одной из важнейших частей любого языка программирования для ЭВМ является редактор, который служит для ввода и
видоизменения программы, в этом отношении Форт не исключение. Удобной особенностью Форта является то, что редактор
написан на Форте и может, как и всякая другая программа, легко изменяться с целью адаптации к вашим нуждам и вкусам.
Так как редактор — это программа, которую вы будете использовать при программировании на Форте наиболее часто, имеет
смысл изменить его, если вы не удовлетворены редактором, который вам достался вместе с системой.
Попыток стандартизировать Форт-редактор не предпринималось, за исключением того, что слово EDITOR используется в
качестве имени словаря редактирующих операторов (словари рассмотрены в гл.14). При таком разнообразии на рынке аппа¬
ратуры (в дополнение к разнообразию мнений о том, что такое хороший редактор), достаточно бессмысленна попытка навя¬
зать редактор, приемлемый для всех.
Существует два типа редакторов, поставляемых обычно с Фортом (часто с одной и той же системой). Первый из них
строчный редактор, который в соответствии с названием дает возможность модифицировать содержимое блоков Форта
строчка за строчкой (предполагается, что блок Форта содержит 16 строк по 64 символа). Хотя строчный редактор занимает
мало места в памяти и обладает высоким быстродействием, ему свойственны ограничения, связанные с тем, что вы не видите
изменений, которые вносите в текст блока в целом. Вторым типом Форт-редактора является экранный редактор, где моди¬
фикации происходят в месте текста, отмеченном курсором, который можно переместить куда угодно в пределах блока, ото¬
браженного на экране. Экранный редактор проще использовать, чем строчный, хотя и за счет большего объема занимаемой
памяти.
В этой главе мы рассмотрим типичный, но довольно простой строчный редактор, а затем предложим исходный текст эк¬
ранного редактора. Если у вас имеется только строчный редактор, вы сможете егр использовать для написания предложенно¬
го здесь экранного редактора. Владея работающим экранным редактором, вы можете его использовать для упражнений,
предлагаемых в книге, так же как и для совершенствования самого редактора. Даже если вы вполне удовлетворены вашим
редактором (многие версии Форта снабжены редакторами, более совершенными, чем приведенный здесь нами), вам следует
рассмотреть некоторые наши идеи, прежде чем переходить к гл.13, где описана структура редактора.
Основы редактирования для Форта
Как было показано в гл.10, ввод-вывод для диска осуществляется через резервные зоны памяти,
называемые блочными буферами. Как вы знаете, ключевым словом для ввода с диска служит
BLOCK, которое загружает содержимое блока с диска в свободный блочный буфер и заносит в стек
адрес первого байта этого буфера. Напомним также, что ключевыми словами для спасения блоков
информации на диске являются UPDATE, которое помечает буфер блока, чтобы он был спасен, и
FLUSH (или SAVE-BUFFERS), которое записывает на диск содержимое буферов, помеченных
UPDATE.
Редактирование производится путем переноса содержимою блока с диска в буфер, изменения со¬
держимою этого буфера и пометки буфера таким образом, что его измененное содержимое будет
снова записано на диск. В действительности при работе с экранным редактором текст из буфера пе¬
реносится в другую область памяти, где производится редакторская правка до возвращения текста
обратно в буфер. В этом случае измененный текст из буфера редактора переносится в блочный бу¬
фер только при условии, что сделанные редактором изменения следует занести на диск. Если текст
всегда редактируется непосредственно в блочном буфере, вы будете вынуждены использовать
EMPTY-BUFFERS каждый раз , когда вы решите отменить некоторые редакционные исправления.
Измененный текст в буфере редактора должен быть перенесен в блочный буфер прежде, чем он
сможет быть записан на диск, таким образом уменьшится вероятность занесения на диск нежела¬
тельных изменений.
Строчное редактирование
Строчный редактор Форта состоит из набора слов, которые используются для изменения содер¬
жимого блоков Форта строчка за строчкой. Строчный редактор не является ’’унифицированной”
программой, в которой каждое слово, предназначенное для редактирования строки, применяется не¬
зависимо для изменения отдельной строки текста. Команда n LIST в начале процедуры редактиро¬
вания присваивает номер блока переменной SCR и отображает содержимое блока, который будет
редактироваться (SCR не нужно для системы Форт-83, но большинство диалектов ее используют).
6 М. Келли
161
Слова строчного редактора используют число в SCR для идентификации блока, который должен ре¬
дактироваться, и, кроме того, многие из них требуют при исполнении наличия в стеке номера стро¬
ки. В табл. 12.1 представлены некоторые типичные команды строчного редактора, которые исполь¬
зовались в устаревшей версии MMSFORTH V 1.9 (MMSFORTH в настоящее время поставляется с
мощным экранным редактором). Вы сами можете описать эти слова, основываясь на знаниях, при¬
обретенных в гл.10.
Таблица 12.1. Команды строчного редактора
MSFORTH V 1*9.
n А текст Добавить строку, сдвинуть вниз
остальные строки,
строка помещается в PAD-буфер
n D Стереть строку, сдвинуть вверх
остальные строки;
строка помещается в PAD-буфер
n I Ввести строку, сдвинув
остальные строки вниз;
строка извлекается из PAD-буфера
n P текст Поместить текст, следующий за "P",
в строку n
n R Заменить строку n строкой
из PAD-буфера
S Выйти из редактора и спасти
все изменения
n T Отобразить на экране строку n;
поместить строку в PAD-буфер
Q Выйти из редактора без спасения
внесенных изменений
При выполнении команды LIST номер блока запоминается в SCR, а ею содержимое заносится в
буфер. Большинство операторов строчного редактора используют последовательность SCR @
BLOCK для получения адреса памяти, куда был загружен блок с диска (блочный буфер). Редакти¬
рование производится непосредственно в блочном буфере, и дополнительно требуется только 64
байта памяти сразу за словом PAD. Эта память служит для размещения строки, которая стерта или
введена, так что она может быть позднее использована для ввода или замещения фрагмента текста
в блочном буфере. Простота этого редактора привлекательна, но его возможности, очевидно, весьма
ограничены.
Более мощные строчные редакторы снабжены командами для поиска, добавления или замещения
фрагмента текста в пределах строки, блока или серии блоков. Эти или другие улучшения мо-
гутсделать строчное редактирование более гибким и эффективным. Простой строчной редактор за¬
нимает очень мало места в памяти и работает на любом дисплее. Но даже после улучшения строч¬
ный редактор не сможет сравниться в простоте использования с экранным редактором.
Экранное редактирование
Экранный редактор представляет собой программу (в противоположность совокупности слов, со¬
ставляющих строчный редактор), которая загружает содержимое блока в буфер, отображает текст и
позволяет изменить текст в соответствии с положением курсора на экране. Все вводы с клавиатуры
перехватываются редактором, который распознает вводимый код и выделяет коды, генерируемые
командными клавишами, в то время как все прочие ’’печатные” символы используются для измене¬
ния текста. Экранный редактор просто использовать, так как измененный текст всегда на экране, а
применение курсора для отметки места, где надлежит что-то изменить, является вполне естествен¬
ным. Экранный редактор может быть дополнен многими новыми возможностями, такими как до¬
полнительные строчные буферы, так называемые ’’теневые блоки” для обширных комментариев
(см.гл.10), автоматическая простановка даты (дата ставится на верхней строке), средства для чте¬
ния и ввода двоичных величин при редактировании блоков данных и многое другое.
162
Экранный редактор
Наш экранный редактор требует, чтобы ваша версия Форта и (или) ваш видеотерминал имели
некоторый контроль над тем, что отображается на экране. Как минимум, вы должны быть способны
очистить экран и поместить курсор в верхний левый угол, а также установить курсор в указанную
точку экрана. Способы реализации этих функций сильно варьируются от ЭВМ к ЭВМ и от терми¬
нала к терминалу. Большинство удаленных терминалов воспринимают последовательности управля¬
ющих символов, посланных оператором EMIT, и за счет этого управляют изображением на экране.
Многие ЭВМ со встроенными дисплеями также воспринимают управляющие символы, и кроме того,
работают со словами Форта, предназначенными для перемещения курсора и управления экраном.
Например, MMSFORTH использует слово PAGE для очистки экрана и РТС для перемещения кур¬
сора в позицию, заданную числами в стеке. Мы написали этот редактор для работы с терминалом
Lear-Siegel ADM-31, и если вы используете другой терминал, вам надо модифицировать программу
в блоке 2.
Кроме минимальных возможностей по очистке экрана и перемещению курсора многие терминалы
(встроенные дисплеи) имеют много других функций. Например, часто бывает предусмотрена воз¬
можность стирания символов в интервале от места, отмеченного курсором, до края строки переме¬
щения курсора вверх или вниз на одну строку и т.д. Если ваш дисплей позволяет делать такие ве¬
щи, то вы можете сделать работу редактора более удобной, хотя эти усовершенствования, строго го¬
воря, и не являются обязательными. Мы дадим вам советы, как выполнить эти модификации. Нако¬
нец, некоторые встроенные дисплеи имеют ЗУ, в которых изображение хранится в виде ASCII сим¬
волов и которые непрерывно опрашиваются с помощью схемотехнических средств, а их содержимое
отображается на экране. Если ваша ЭВМ работает таким образом, можно сделать намного более эф¬
фективный редактор. Мы дадим вам некоторые предложения по поводу такого редактора в гл.13,
где обсудим структуру этого редактора.
Приведенный ниже текст программы редактора содержит различные советы по модификациям,
которые могут сделать его использование более удобным и эффективным. Программа составлена,
однако, с тем расчетом, чтобы максимально упростить ее применение и сделать это возможным на
максимальном числе ЭВМ. Мы полагаем, что вы используете ее сначала только с совершенно
необходимыми, которые совершенно необходимыми, т.е. изменив лишь
0 ( 20 июля 85 Экранный редактор NS 01 из 10)
1 TASK ; DECIMAL
2
3
32
CONSTANT
BL
1024
CONSTANT
1K
4
64
CONSTANT
64
63
CONSTANT
63
5
10000
CONSTANT
PDELAY
5000
CONSTANT
SDELAY
6
VARIABLE
SCR
VARIABLE
R0W
7
VARIABLE
C0L
VARIABLE
I/R
8
VARIABLE
L0WBLK
VARIABLE
HIGHBLK
9 ( запоминание первого и последнего блоков, которые можно редактировать на вашей ЭВМ)
10 1 L0WBLK ! 169 HIGHBLK !
11 : ; ( слово, не выполняющее никакой работы)
12 : MODE ( — ) I/R @ 0=I/R 1 ; ( выбор ввод-замещение )
13 : DELAVS ( n— ) 0 DO LOOP ; ( задержка на n циклов )
14 : PAUSE ( — ) PDELAY DELAYS ; ( длинная задержка)
15 SPAUSE ( — ) SDELAY DELAYS , ( короткая задержка )
0 ( 20 июля 85 Экранйый редактор NS 02 из 10)
1 : PADDR ( n—) CREATE , D0ES> @ PAD + ;
2 IK 2 * PADDR BLINE ( последняя строка кольцевого буфера)
3 IК 2 * 64 + PADDR LBUFF ( адрес строки для копирования-замещения)
4 : C0NTR0L ( c-) CREATE DEPTH DUP С. 0 D0 DEPTH R0LL С.
5 L00P D0ES> DUP DUP C@ + SWAP D0 I 1+ C@ EMIT L00P ,
6 27 42 C0NTR0L <CLR> ( очистка экрана, курсор вверх влево)
7 : <CXY> 27 EMIT 61 EMIT R0W @ 32 + EMIT C0L @ 32 +
6’
163
EMIT ;
8 ( положение курсора в строке X и столбце Y)
9 (: <CLR> PAGE ; : <CXY> ROW @ COL @ PTC ;)
10 ( Чтобы использовать далее <CXY>, <HMC>, <DLF>,
и <ADV>)
11 ( необходимо описать их здесь, используя лишь в случае
12 отсутствия пряного позиционирования курсора)
13 ( 28 CONTROL <HMC> ( перемещение курсора вверх влево)
14 ( : <CXY> <HMC> ROW @ ?DUP IF 0 DO <DLF> LOOP THEN)
15 ( COL @ ?DUP IF 0 DO <ADV> LOOP THEN ;)
описания <CXY> и <CLR> в блоке 2. Вы можете позднее использовать сам редактор для внесения
последующих модификаций, которые допускает ваша ЭВМ, ваша версия Форта и ваш дисплей.
Хотя это весьма простой редактор, ota включает в себя некоторые функции, которые полезны и в
то же время необычны. Даже если вы удовлетворены редактором, поставленным с вашей версией
Форта, для вас будет, вероятно, полезно изучить эту программу, особенно в связи с тем, что мы ис¬
пользуем ее в качестве примера того, как следует укладывать и комментировать исходный текст
программы.
Если ваша версия Форта содержит пользовательскую переменную SCR, вам следует удалить опи¬
сание SCR со строки 6. Вы должны также заменить числа 1 и 169 в строке 10 на номера первого и
последнего блоков, которые может модифицировать редактор в вашей системе.
CONTROL представляет собой слово-описатель, которое формирует слова, предназначенные для ге¬
нерации последовательностей управляющих символов, число которых задается числом в стеке. Хотя
это слово используется здесь только один раз (для <CLR> в строке 9), оно включено в текст, чтобы
позволить вам описать другие управляющие последовательности. Слово <CLR> определено так, что
оно посылает на терминал <EMIT> символ 27, за которым следует код 42. Описания <CLR> и
<CXY> в строках 6, 7 и 8 выполнены для терминала ADM-31 и, вероятно, должны быть изменены
для вашего оборудования. Описание <CXY> в строке 7 показывает типовой способ прямого позицио¬
нирования курсора. ADM-31 требует ESC-последовательности из четырех символов вида
ESC ”=” (строка) + 32 (столбец) + 32,
где строка (row) и столбец (column) равны номерам строки и столбца, куда должен быть установлен
курсор (0 — 23 и 0 — 79).
Когда ESC и ”=” (ASCII коды 27 и 61) переданы, к кодам строки и столбца добавляется 32, после
чего они посылаются на терминал. Редактор использует переменные ROW и COL для отслеживания
положения курсора.
Если ваша версия Форта содержит слова для очистки экрана и перемещения курсора, вы захоти¬
те использовать описания на строке 9 (не забывайте поместить в скобки прежние описания). В
MMSFORTH слово PAGE очищает экран и устанавливает курсор в верхний левый угол, в то время
как РТС устанавливает курсор в положение, заданное кодами в стеке. Вам следует подставить эк¬
вивалентные слова из вашей версии Форта. Если у вас нет средств для непосредственного управле¬
ния положением курсора с помощью слова типа РТС или управляющих кодовых последовательно¬
стей, вы можете использовать строки 13 и 14,
0 ( 20 июля 85 Экранный редактор NS 03 из 10)
1 : @CURS0R ( —г с ) R0W @ C0L @ ; ( вызов строки и столбца )
2 : !CURS0R ( г с — ) C0L ! R0W ! ; ( запоминание столбца и строки)
3 ( Опишите здесь, если возможно. <STL> и <HMC>
и используйте в блоках 3 и 4)
4 : <S0L> 0 C0L ! <CXY> ; ( перемещение курсора в начало строки )
5 ' <H0M> 0 0 !CURS0R <CXY> , ( перемещение курсора вверх влево )
6
BAB0VF.
( —n
)
R0W @ 64 * ; (
число байтов в рядах выше курсора )
7 :
: BBEL0W
( —n
)
16 R0W @ - 64 *
1K + ; ( байт ниже курсора )
8
g
: 0FFSET
( —n
)
BAB0VE C0L @ + ;
( число байт в буфере перед курсором )
10 :
: CP0S
( —адр
) PAD OFFSET +
, ( адрес курсора )
11
: LSTART
( —адр
) PAD BAB0VE +
; ( адрес начала строки )
164
12 : LEND ( —адр ) LSTART 63 + ; ( адрес конца строки )
13
14 BLEFT ( —n ) LEND CPOS - , ( число байтов в строке слева от курсора )
15
но вам нужно сначала описать слова <HMC> для перемещения курсора в верхний левый угол (без
стирания чего-либо), <DLF> для сдвига курсора вниз на одну строку и <ADV> для перемещения
курсора на одну позицию. Эти слова могут быть описаны с помощью управляющих кодовых после¬
довательностей, которые описаны с привлечением CONTROL. Эти модификации программы для
<CXY> и <CLR> — единственное, что необходимо, чтобы заставить редактор работать на любом
дисплее, который отображает по крайней мере 16 строк по 64 символа в каждой. Изменения, пред¬
лагаемые для других блоков, являются чисто рекомендательными, но сделают работу с редактором
0 ( 20 июля 85 Экранный редактор NS 04 из 10 )
1 ( Если возможно, опишите <BSP>,<ADV>,<ULF>,<DLF> здесь )
2 ( для того, чтобы использовать вместо <CXY> в описаниях )
3 ( в строках 4, 5, 6 и 7. )
4 : LEFT COL @ 0 > IF “1 COL +\ <CXY> ( или <BSP> ) THEN ;
5 : RIGHT COL @ 63 < IF 1 COL +! <CXY> ( или <ADV> ) THEN ;
6 : UP ROW @ 0 > IF -1 ROW +! <CXY> ( или <ULF> ) THEN ;
7 : DOWN ROW @ 15 > IF 1 ROW +! <CXY> ( или <DLF> ) THEN ;
8 : <EEL> ( — ) BLEFT SPACES <CXY> ; ( стереть конец строки )
9 : NEWLINE ( — ) <SOL> DOWN ; ( курсор в начало следующей строки )
10 . SHOWLINES ( — ) @CURSOR 16 ROW @ ( отображение строк )
11 DO I ROW ! < SOL> LSTART 64 _TRAILING TYPE <EEL> LOOP
12 ! CURSOR <CXY> ;
13 : SNOWBLK ( — ) @CURSOR <HOM> SHOWLINES ! CURSOR <CXY>;
14 : TYPELINE ( — ) CPOS BLEFT 1+ OVER OVER TYPE _TRAILING
15 SWAP DROP BLEFT 1+ - SPACES <CXY> ,
удобнее и производительнее.
Ваш видеодисплей или терминал может воспроизводить управляющие кодовые последовательно¬
сти для перемещения курсора в начало строки или в верхний угол экрана. Если это так, то вам сле¬
дует использовать CONTROL для описания <STL> так, чтобы перемещать курсор в начало строки,
и <HMC> для установки ею вверх влево. Эти слова могут затем использоваться вместо <CXY> в
описаниях <SOL> и <HOM> в строках 4 и 5. Это сделает редактирование более удобным и быстрым.
Редактор будет более быстрым и удобным, если ваше оборудование позволяет и вы можете заме¬
нить <CXY> в строках 4-7 словами, описанными в строках с 1-й по 3-ю или ранее. <BSP> должно
перемещать курсор на предшествующую позицию, <ADV> — на одну позицию вперед. Аналогично
слово <ULF> должно перемещать курсор вверх на одну строку. Эти слова могут, вероятно, быть
описаны с помощью CONTROL. Ваше оборудование может также позволять замену описания
<EEL> в строке 8 на управляющую последовательность, описанную через CONTROL.
0 ( 20 июля 85 Экранный редактор NS 5 из 10 )
1
2 : BCLEAR ( — ) PAD 1K + 1152 BL FILL ; ( очистка буфера )
3 : SCLEAR ( — ) PAD 1K BL FILL SHOWBLK ; ( очистка экрана )
4
5
6 : LOADBLK ( — ) SCR @ BLOCK PAD 1K CMOVE ;( загрузка блока )
7 : RESTORE ( — ) LOADBLK SHOWBLK ;
8
9 : (UPDATE) ( — ) PAD SCR @ BLOCK 1K CMOVE UPDATE ;
( пометка для спасения )
10
11 : +BLK ( — ) SCR @ HIGHBLK @ < IF 1 SCR +! RESTORE
THEN ;
12 : -BLK ( — ) SCR @ LOWBLK @ > IF -1 SCR +! RESTORE
165
THEN ,
13
14
: CLRTOP (
— r с ) @CURSOR <HOM> <EEL> ;
15
: WRITETOP
( r с — ) <HOM> TYPELINE !CURSOR <CXY>
0 (
20 июля
85
Экранный редактор NS 06 из 10 )
1 :
?BLK# (
-- )
CLRTOP ." * * * BLOCK#: ” SCR @ .
( блок# )
2
о
PAUSE WRITETOP ;
о
4 :
UPDATES
( —
) (UPDATE) ( пометить блок и показать это
5
CLRTOP
. ” * * * UPDATED В lock#: ” SCR @ .
fZ
SPAUSE WRITETOP;
О
7 :
?CLEAR (
— )
CLRTOP ." * * *” X-OUT. (B)uffer,
(S)creen ?”
8
KEY DUP
66 =
IF DROP BCLEAR
9
ELSE 83 = IF SCLEAR !CURSOR <CXY> EXIT
THEN
10
THEN WRITETOP ,
11 :
?EXIT (
f~)
CLRTOP .” * * *" EXIT:(S)ave, (Q)uit ?”
12
KEY DUP
83 =
13
IF DROP
DROP
DROP UPDATE FLUSH 1+ EXIT
14
ELSE 81
= IF
DROP DROP EMPTY-BUFFERS 1+ EXIT THEN
15
THEN WRITETOP ,
0 ( 20 июля 85 Экранный редактор NS 07 из 10)
1 : 0PENUP ( — ) ( раздвинуть текст в месте буфера, куда указывает курсор)
2 COL @ 64 < IF CP0S DUP
3 1+ BLEFT <CM0VE (или CM0VE>) BL CPOS С! THEN ;
4
5 : OPEN ( — ) OPENUP TYPELINE ; ( раздвинуть текст в месте, указанном курсором)
6 : TRUNC ( — ) <EEL> CPOS BLEFT BL FILL; ( укоротить строку)
7
8 : OVERTYPE ( СИМВ— ) COL @ 64 < ( заместить
символ, на который указывает курсор)
9 IF DUP EMIT CPOS С! COL + ! ELSE DROP THEN ;
10
11 : INSERT ( СИМВ— ) OPENUP OVERTYPE TYPELINE ;
( ввести символ )
12 : DELETE ( — ) COL @ 64 < ( стереть символ )
13 IF CPOS 1+ CPOS BLEFT CMOVE BL LEND С!
14 <EEL> TYPELINE
15 THEN ;
0 ( 20 июля 85 Экранный редактор NS 08 из 10)
1 : CLINE ( — ) LSTART LBUFF 64 CMOVE ; ( копирование строки в буфер)
2
3 : PLINE ( — ) LBUFF LSTART 64 CMOVE ( выложить строку буфера)
4 @CURSOR <SOL> TYPELINE !CURSOR <CXY> ;
5
6 : KLINE ( — ) ( стереть строку, занеся ее в кольцевой буфер)
7 LSTART BLINE 64 CMOVE ( перенос текущей строки )
166
8 LSTART 64 + LSTART BBELOW CMOVE ( сдвинуть буфер вверх )
9 SHOWLINES ; ( отображение измененных строк )
10
11 . ILINE ( — ) ( ввести строку из кольцевого буфера )
12 LSTART DUP 64 + BBELOW <CMOVE (или CMOVE>)
( сдвинуть буфер )
13 BLINE LSTART 64 CMOVE ( перенести строку с низа буфера )
14 SHOWLINES ; ( отображение измененных строк )
15
0 ( 20 июля 85 Экранный редактор NS 09 из 10)
1 CREATE KEYVECTORS ] ( исполнительный вектор команд редактора)
2 LEFT ( А курсор влево ) BLK# ( в номер блока )
3 CLINE ( С копирует строку ) DELETE ( D стирает символ )
4 9EXIT ( E уход из редактора ) ( F )
5 MODE ( G переход в новый режим )—( H )
6 ILINE ( I ввод строки ) ( J )
7 KLINE ( К стирание строки ) - BLK ( L последний блок )
8 NEWLINE ( M или ENTER, CR+LF )+BLK ( N следующий блок )
9 OPEN ( 0 раздвинуть текст ) PLIHE ( P вставить строку )
10 <HOM> ( Q курсор на место ) RESTORE ( R восстановить экран )
11 RIGHT ( S курсор вправо ) TRUNC ( T укоротить строку )
12 UPDATES ( U пометить буфер ) ( V )
13 UP ( W курсор вверх ) ?CLEAR ( X очистить буфер экрана )
14 ; ( Y ) DOWN ( Z курсор вниз )
15 : KEYD0 ( п— ) 1- 2* KEYVECTORS + @ EXECUTE;
0 ( 20 июля 85 Экранный редактор NS 10 из 10)
1 : EDITCASE ( флаг симв— флаг’ )
2 DUP 27 < OVER 0 > AND ( легальный управляющий символ9 )
3 IF KEYD0 ( если так, то исполняем команду )
4 ELSE DUP 31 > OVER 127 < AND ( если нет, печатный символ ? )
5 IF I/R @ ( если да, смотрим, каков режим )
6 IF INSERT ELSE OVERTYPE THEN ( и вводим или замещаем )
7 ELSE DROP ( но, если символ непечатный, игнорируем )
8 THEN
9 THEN ;
10
11 : EDINIT ( blk— ) SCR ! EMPTY-BUFFERS LOADBLK
SHOWBLK;
12 : EDITL00P ( — ) EDINIT 0 BEGIN KEY EDITCASE DUP
UNTIL
13 DROP ,
14 : EDIT ( b1k— ) <HOM> <CLR> EDITL00P <HOM> <CLR> ;
15 : E SCR @ EDITX ;
Многие терминалы и ЭВМ позволяют описать слово, которое генерирует звуковой сигнал. Это ча¬
сто делается путем выдачи кода CTRL G или ASCII 7, называемого BEL. Вы можете описать <BEL>
как
7 CONTROL <BEL>
167
или что-нибудь эквивалентное. Если это так, вы можете заменить ”—” в блоках 9 и 10 словом
<BEL>, и ваша ЭВМ выдаст вам звуковойхигнал, если вы напечатаете неописанный управляющий
или другой символ, который нельзя использовать.
Использование экранного редактора
Теперь поговорим об использовании редактора. Загрузите блоки редактора и, чтобы отредактиро¬
вать блок с номером ”п”, выдайте команду n EDIT. Оформленный соответствующим образом блок
появится на экране, курсор будет помещен в верхнем левом углу. Раз SCR содержит номер нужно¬
го блока (а так должно быть после выполнения вами процедур EDIT или LIST для этого блока), вы
просто нажимаете ”Е” для редактирования данного блока. Используя команды, перечисленные в
табл. 12.2, вы сможете изменить содержимое блока различными способами, пока не получите удов¬
летворительного результата путем спасения этих изменений или игнорируя их. Экспериментируйте
с редактором, чтобы привыкнуть к нему.
Таблица 12.2. Команды экранного редактора, клавиши и их функции
Команды позиционирования курсора
Q
Курсор
на место
- в верхний левый угол
W
Курсор
вверх на
одну строку.
S
Курсор
вправо.
z
Курсор
вниз.
А
Курсор
влево.
M
Курсор
в начало
следующей строки (клавиша <ВК>делает то же самое)
Команды редактирования символов
0 Раздвижка текста, смещение остальной строки на один символ.
D Стирание, смещение остальной строки на один символ влево).
T Укорачивание, стирание строки справа от курсора.
G Установка режима переключения между режимами ввода и замещения.
В режиме замещения буква, на которую указывает курсор, заменяется напечатанной
буквой.
В режиме ввода напечатанная буква вводится в текст,
остальная часть строки смещается вправо.
Команды редактирования строк
С Копирование текущей строки в буфер строки.
P Извлечение строки из буфера и укладка ее на место текущей строки.
К Стирание строки и помещение ее в кольцевой буфер (строка не теряется).
1 Вывод строки из кольцевого буфера.
Команды редактирования блока
U Перемещение буфера редактора в блочный буфер и пометка его
для записи на диск (UPDATE).
R Восстановление буфера редактора (стирание изменений, восстановление
последней записанной версии).
L Переход к редактированию последнего (предшествующего) блока.
N Переход к редактированию следующего блока.
В Отображение номера блока, редактирование которого производится.
X Заполнение пробелами (X-out) кольцевого буфера или экранного или никакого.
E Уход из редактора с (без) записи на диск текста
(вам будет предоставлен выбор).
Необычной особенностью редактора является наличие так называемого ’’кольцевого буфера”, ко¬
торый позволяет не терять строки текста, подлежащего уничтожению (стиранию). Когда очередная
строка удаляется, она копируется в область ”ниже” экрана, и, если стерто достаточно много строк,
они могут снова появиться на экране снизу. Если вы поупражняетесь с процедурами стирания и
восстановления строк, станет понятно, что на экране отображается только верхняя половина глав¬
ного буфера, используемого редактором. Часть ниже экрана никогда не отображается непосредст¬
венно, но ее содержимое может быть просмотрено путем стирания 16 строк (при курсоре на верх¬
ней строке), чтобы содержимое буфера попало на экран. Теперь исходное содержимое экрана нахо¬
дится в буфере. Стирание или ввод 16 новых строк ’’вращает”, буфер в прямом или обратном на¬
правлении, что позволяет восстановить исходное состояние буфера. Буфер редактора имеет кольце¬
168
вую структуру, так что строки не могут быть потеряны. Эта мера облегчает стирание до 16 строк,
чтобы избавиться от них или чтобы переместить их в другой блок, введя их туда.
Существуют два способа, чтобы подавить заполнение ’’скрытого” буфера стертыми строками, ко¬
торые вы не желаете хранить. Команда X-out заполнит все 16 строк скрытого буфера пробелами,
если в ответ на запрос вы нажмете клавишу ”В”. (Если же вы откликнетесь, нажав клавишу ”S”,
пробелами будет заполнен экран. При нажатии любых других клавиш ничего не произойдет.)
Если вы хотите избавиться от одной строки, укоротите ее перед стиранием, что исключит загро¬
мождение буфера ненужными строками. Использование кольцевого буфера станет вполне простым
после того, как вы поупражняетесь с ним.
Существует много способов улучшить редактор, но за счет усложнения текста программы и уве¬
личения требуемой памяти. Некоторые модификации будут предложены в гл.13, но кое-что следует
упомянуть здесь. Как уже было сказано, чисто косметической является замена ”—” в блоках 9 и
10 на <BEL>, после чего редактор будет сигнализировать вам, если нажата неописанная управляю¬
щая клавиша или поступил символ, который не может быть отображен на дисплее. Вы можете ис¬
пользовать управляющие символы из нижнего регистра путем добавления нескольких битов в маску
(см. гл.З).
Если вы можете заменить символ курсора на вашем терминале, вам следует изменить описание
MODE в блоке 1, чтобы использовать курсор для индикации действующего режима ввод-замещение.
А если вы хотите сменить функции командных клавиш редактора, поменяйте местами слова в мас¬
сиве KEYVECTORS.
Существуют и другие улучшения, которые можно добавить, если ваш дисплей может отображать
24 строки по 80 символов. Например, блок, подлежащий редактированию, может размещаться в
центре экрана. Границы блока могут быть выделены, а неиспользуемая область вокруг блока может
быть использована для отображения номера блока, режима ввода и даже меню команд редактирова¬
ния. Редактор может быть модифицирован (с определенным трудом) для использования на терми¬
налах, которые отображают менее 1024 символов, но многие преимущества экранного редактора
при этом будут утрачены, так как весь блок не будет отображен на экране.
Ясно, что используемый вами редактор будет сильно влиять на ваше отношение к Форту. В рав¬
ной мере верно и то, что свойства редактора, кажущиеся одним важными, могут рассматриваться
другими как излишние (и наоборот), так как редактирование в высшей степени индивидуальный
процесс. В гл.13 исследуются причины, почему редактор написан так, а не иначе, даются коммента¬
рии и оригинальные тексты программы. После прочтения главы вы должны знать достаточно о на¬
писании редактора, для того чтобы модифицировать то, что мы предложим, или чтобы добавить
функции к редактору, поставленному вместе с вашим Фортом. Если вы почувствовали, что некото¬
рые свойства вашего редактора вас не устраивают, вы не должны проклинать Форт — в ваших ру¬
ках средства, чтобы написать редактор, который удовлетворит вас в полной мере.
Глава 13
ПРОГРАММИРОВАНИЕ НА ФОРТ. СТИЛЬ
Форт может изменить ваше представление о программировании. Помимо очевидного отличия Форта от других языков
(постфиксная, а не прямая нотация, расширяемость, а не фиксированный набор команд), вы можете почувствовать, что
Форт открывает новые подходы к решению проблем. В этой главе мы рассмотрим стиль программирования Форта как с точ¬
ки зрения решения проблем с помощью программирования, так и с позиции привычек, делающих программирование легче и
продуктивнее.
В этой главе подробно рассматривается редактор, представленный в гл. 12. Вместо того чтобы просто анализировать, как
работает редактор в целом, мы проведем вас через программу и опишем цепочку решений, которые вынудили нас написать
редактор именно так. Вы узнаете больше, если мы скажем вам, как мотивировалось наше решение при написании програм¬
мы, чем если бы мы только объяснили, каким образом работает редактор. Программирование - в равной мере искусство и
наука, и невозможно просто дать правила, которым можно следовать слепо. Каждый программист решит проблему по-разно-
му, и гибкость Форта поощряет эту индивидуальность. Итак, раз вы изучаете редактор, попытайтесь подумать о других воз¬
можных путях решения проблемы.
Сначала проблема должна быть рассмотрена в целом. Затем следует разработать слова низкого уровня, которые обеспечат
связь между оборудованием ЭВМ и словами высокого уровня, выполняющими многие программные функции. (Конечно,
многие из таких слов низкого уровня являются уже частью словаря Форта.) Полезная работа Форт-программы большей час¬
тью выполняется словами, которые занимают положение между примитивами и словами высокого уровня, которые связыва¬
ют все воедино. Создание этих слов среднего уровня всегда предполагает некоторое число проб и ошибок как в отладочных
программах, так и в корректировке неправильного представления об исходной проблеме и о том, как подогнать отдельные
части друг к другу, чтобы решить эту проблему. Работа этого метода проб и ошибок станет ясна, если вы посмотрите, как мы
написали редактор.*
Задание на программу
Если вам рассказали в деталях, что программа должна делать и как она должна это делать,
обычно довольно просто ее написать. В действительности вы не пишите программу, а только транс¬
лируете набор инструкций из одной формы (словесное описание) в другую (программа Форт). Хотя
процесс трансляции может потребовать сосредоточенности, простое написание программы согласно
спецификациям - занятие менее увлекательное и менее творческое, чем выполнение всей работы с
самого начала. Если вы отвечаете за задание и за написание программы, вы имеете возможность
изменить функцию программы в процессе ее написания. Это то место, где начинается творчество, и
Форт этому поможет. Так как вы описали слова, то лучше представляете пути решения ваших про¬
блем и возможности вашей программы.
Программирование является упражнением в решении проблем. Нужно не только понять задачу,
но и заставить ЭВМ выполнять процедуру, необходимую для решения задачи. Это включает в себя
несколько довольно расплывчатых этапов: описание всей проблемы и цели; разделение проблемы на
меньшие задачи, решаемые независимо, и, может быть, последующее деление таких задач; написа¬
ние программы для этих мелких задач; отладка программы, возможно изменение алгоритма для ре¬
шения главной проблемы или составляющей ее части и снова отладка; сборка составных частей;
окончательная отладка всего пакета. Многие языки маскируют этот естественный процесс: описыва¬
ется набор алгоритмов, определяющих все, что должна делать программа, а затем пишется про¬
грамма с минимальными по возможности переделками и отладкой.
Форт допускает другой подход; внутренняя природа решения проблемы при этом более прозрач¬
на. Мелкие проблемы могут решаться одна за другой, проверяться, корректироваться и, может
быть, если надо, при этом модифицируются другие части программы как следствие обстоятельств,
выявленных в процессе отладки. Это возможно в Форте, так как в нем весьма быстро и легко мож¬
но изменить и проверить описание слова или нескольких слов. Мы использовали эту итеративную
модификацию слов несколько раз в предшествующих главах частично как способ показа того, как
строить слова на базе основополагающих идей, частично для того, чтобы привести примеры процес¬
са написания Форт-программ. Составление программы на Форте включает в себя написание некото¬
рого числа коротких описаний, использование их в других описаниях и, наконец, сборку всей сис¬
темы в целом обычно в виде одного слова, которое исполняет программу. Но прежде
170
чем что-либо из этого можно было сделать, вы должны иметь очень точное представление о том,
что должна делать про
грамма, т. e. что является главной задачей. Проблема должна быть сформулирована таким обра¬
зом, чтобы ее можно было поделить на задачи, которые решаются путем описания слов Форта.
Это возвращает нас снова к заданию на программу к описанию того, что должна делать програм¬
ма. Описание высшего уровня представляет ваши интуитивные цели: что бы вы хотели, чтобы дела¬
ла программа? Точное описание ваших намерений является наиболее важным шагом в написании
программы, так как диапазон ваших целей определяет то, насколько будет полезна ваша программа
или даже следует ли ее вообще писать. Этот этап постановки задачи и ошибка здесь будет усили¬
ваться по мере вашей работы и может сделать вашу проблему неразрешимой.
Мы решили написать экранный редактор для этой книги не потому, что было бы полезно для чи¬
тателей его иметь. Много важнее то, что сам процесс написания редактора является хорошим при¬
мером процесса написания любой программы средней сложности. Мы написали редактор для работы
по возможности с простейшим терминалом, так как мы хотели сделать его как можно более универ¬
сальным, имея в виду, что это сильно упростит его использование читателями с другими типами
ЭВМ. Но это ограничение сделало также алгоритм более строгим, а процесс программирования и
саму программу более интересными. Таким образом мы не могли полагаться на известные характе¬
ристики ЭВМ или терминала, что сделало бы работу легче. Какова же задача экранного редактора?
Конечно, концепция экранного редактора уже включает многое из того, что должна делать про¬
грамма. Основной функцией редактора являются вход в режим и модификация текста программы,
но существует много способов выполнения этого. Фактическим стандартом для входа в экранный
редактор Форта является n EDIT. Эта команда должна отображать содержимое блока n, обеспечи¬
вать модификацию содержимого различными путями и последующую запись на диск. Текст изме¬
няется путем перемещения курсора в любом направлении и ввода или стирания символов, на кото¬
рые указывает курсор. Мы бы также хотели, чтобы редактор позволял нам копировать, замещать,
вставлять и стирать строки текста, на которые указывает курсор, а также выполнять некоторые
другие операции. Мы можем теперь установить определенное число точно заданных, но достаточно
общих спецификаций того, что должен делать редактор.
1. Позволять редактировать блок путем выдачи команды EDIT, отображающей содержимое блока в
виде 16 строк по 64 символа и установку курсора в верхний левый угол экрана.
2. Позволять перемещать курсор вправо, влево, вверх и вниз путем нажатия определенных клавиш.
Позволять стирание символов нажатием другой клавиши.
3. Позволять замещать символы, на которые указывает курсор, если включен соответствующий ре¬
жим.
4. Позволять переходить в режим ввода, когда символ не печатается поверх существующего, отме¬
ченного курсором, а вводится так, что остальные символы в строке смещаются, освобождая место
для нового. Позволять также переход в режим ’’замещение” (предположительно переключение из
режима в режим производится с помощью командного символа).
5. Позволять стирать строки текста и заносить их в буфер (используя другой управляющий символ)
так, чтобы имелась возможность ввести где-то еще.
6. Позволять установить флаг записи на диск (опять же посредством управляющего символа), ис¬
пользуя слово UPDATE, чтобы редактируемый блок мог быть записан.
7. Позволять уходить из редактора (посредством еще одного управляющего символа) с последую¬
щим уходом в Форт-интерпретатор. Хотя многие программисты, может быть, и не записывают та¬
кие формальные спецификации, они, несмотря на это, держат в уме достаточно точно определен¬
ные цели. Мы теперь имеем хорошо описанные цели, но еще не знаем, как разбить программу на
слова Форта. Общая задача должна быть сначала разделена на меньшие части. Существует много
способов, как это сделать, а для редактора это может быть сделано в уме. Например, мы знаем, что
должны разделять текстовые и управляющие символы, выполнять определенные процедуры соглас¬
но тому, что требуют управляющие символы, и осуществлять переключение между режимами ввода
и замещения. Часто полезно представить проблему на бумаге. Это можно сделать разными способа¬
ми. Традиционно используется блок-схема. На рис. 13.1 представлена блок-схема редактора. Но
блок-схема занимает много места на бумаге и ее неудобносоздавать и изменять. Словесное описа¬
ние функций редактора, как это показано в табл. 13.1, может быть лучше. (Для подготовки таких
описаний особенно удобен текстовый редактор, который предоставляет широкие возможности для
внесения изменений.)
171
Рис. 13.1
Так как вы программируете на Форте, то можете предпочесть запись описания в форме псевдо-
Форт, как в табл. 13.2. Слова псевдоФорт могут отличаться от тех, что будут использоваться в про-
граме (хотя они могут и предвосхитить имена реальных слов), они имеют то преимущество, что де¬
лят всю проблему наилучшим образом.
172
Загрузить
блок в буфер
Запускается
редактор
Отобразить блок
с курсором в верхнем
левом углу
Сколько буферов?
Начало цикла
Используется
конструкция
BEGIN...UNTIL
Прием символа
Использовать
KEY или TKEY?
Управляющий символ?
Должно быть
определено
Нужен
флаг
Нет
Да
Режим ввода?
Да
Нет
Исполнить команду
редактора
Заместить
символ
Ввести символ,
сдвинуть текст влево
Символ завершения
редактирования?
Нет
Да
Записать текст или нет?
Вернуться в
Форт-интерпретатор
Таблица 13.1. Словесное описание редактора
Загрузить редактируемый блок в блочный
буфер.
Отобразить блок, поместить курсор влево
вверх.
Начать цикл.
Получить символ с клавиатуры.
Это управляющий символ?
Если да, то выполнить команду
редактора.
Если нет, проверить, является
ли он отображаемым.
Если да, то проверить - мы в
режиме ввода?
Если да, то ввести символ в
текст.
Если нет, то произвести
замещение символа.
Если нет, то символ игнори
руется.
Продолжить цикл, пока придет команда
выхода из системы.
Как мы увидим, существует весьма яркая параллель между табл.13.2 и словами, которые будут
использоваться для выполнения основной работы редактора. Существует много методов деления
программных заданий, начиная от формализма блок-схемы и вплоть до идей, которые вы держите в
уме. Вы можете считать полезной комбинацию слов и диаграмм. Важно, чтобы деление на вторич¬
ные задачи было выполнено до начала написания программы. Форт требует меньше формализма
при разделении проблемы на части, чем многие другие языки, но решение любой задачи (безраз¬
лично - программной или нет) выиграет, если она сначала обдумана. Хотя ни одно из этих описа¬
ний редактора не является полным, все они задают базовую стратегию решения проблемы. Каждое
нажатие клавиши должно обрабатываться индивидуально, для того, чтобы отделить команды от
букв, вводимых в текст. Хотя команды редактора не стандартизованы, мы имеем отдельный меха¬
низм для их ввода (используя управляющие клавиши). Мы также решили включить два режима
(ввод и замещение), позволяющие вводить символы двумя различными способами. Редактор будет
работать в бесконечном цикле до тех пор, пока не будет выдана команда EXIT (’’выход”), по кото¬
рой измененный текст будет записан (или нет) на диск. Мы можем теперь рассказать вам, как мы
подходили к написанию программы.
Мы начали с уточнения некоторых деталей нашего общего плана. Редактирующие команды, вы¬
бранные кодом управляющей клавиши, было бы легко описать, если бы использовался исполнитель¬
ный вектор. Это делает команды легко задаваемыми, а для пользователя легко изменяемыми по же¬
ланию. Простейший путь выполнения выбора ввод-замещение открывает введение флага, который
будет устанавливаться одной из команд редактора. Редактор выполняет большинство своих опера¬
ций в пределах цикла BEGIN ... UNTIL отчасти потому, что нужен именно бесконечный цикл (по¬
зволяющий выполнять любое число
Таблица 13.2. Описание редактора на псевдоФорт
: EDIT ( n )
BLOCK-TO-BUFFER BLOCK-TO-SCREEN
CURSOR-TO-START
BEGIN
• FETCH-CHARACTER CONTROL-CHAR?
IF DO-EDIT
ELSE PRINTABLE-CHAR?
IF INSERT-MODE?
IF INSERT
ELSE OVERTYRE
THEN
ELSE DROP
THEN
THEN
END-FLAG UNTIL ;
173
редактирующих операций), отчасти из-за того, что EXIT только меняет флаг, лежащий в стеке,
для того чтобы выйти из цикла при UNTIL. В нашей концепции необходимо еще принять решение
о многих других редактирующих командах и об использовании памяти ЭВМ.
Мы имеем выбор позволить редактору непосредственно модифицировать содержимое блочного бу¬
фера или перенести его содержимое в другую часть памяти (PAD и далее) и редактировать его там.
Был выбран последний метод, так как он позволяет ’’отменять” изменения и таким образом обеспе¬
чивать некоторую защиту против изменений на диске, внесенных случайно. Карта распределения
памяти показана в табл. 13.3. Слово BLOCK используется для загрузки блока, номер
Таблица 13.3. Предварительные соображения о распределении памяти для редактора
Блочный
буфер
Переносится
в
PAD
Отображается Терминал
на
Первая строка —> Первая строка
Вторая строка —> Вторая строка
Последняя строка—> Последняя строка
—>
—>
. —>
—>
Первая строка
Вторая строка
Послед-няя строка
которого хранится в стеке, в блочный буфер, откуда 1024 символа переносятся командой CMOVE в
PAD, где будет проводиться редактирование. Когда нужно сохранить изменения, 1024 символа из
PAD переносятся в блочный буфер, который помечается для сохранения оператором UPDATE. Блок
из 1024 символов отображается на экране терминала. Положение курсора на терминале и положе¬
ние соответствующего символа в PAD оказываются связанными так, что изменение положения кур¬
сора означает изменение указания на символ в тексте, лежащем в массиве выше адреса PAD. Поло¬
жение курсора логически характеризуется кодами строки и столбца, номера которых отсчитываются
от левого верхнего угла дисплея. Эти коды могут быть затем использованы для получения любой
другой информации, связанной с положением курсора.
Одной из особенностей редактора, в которой мы нуждались, была возможность вводить и стирать
строки текста, на которые указывает курсор. Память сразу за последней отображенной строкой (на¬
чиная с PAD+1024) может использоваться для хранения бесконечного числа стертых строк, которые
могут быть вставлены в отображаемый текст, если это желательно.
Таблица 13.4. Использование памяти редактора
Блочный Перено- PAD Отобража- Терминал
буфер сится в ется на
Перая строка —> Первая строка —> Первая строка
Вторая строка —> Вторая срока —> Вторая строка
—> —2>
Последняя строка —> Последняя строка —> Последняя строка
1-я скрытая строка
2-я скрытая строка
16-я скрытая строка
Дополнительная строка
Строка для операций копирова
ния-замещения
Представляется разумным включить 16 строк в скрытый буфер так, чтобы содержимое всего блока
могло быть стерто и затем восстановлено где-то еще. Если процедуры ввода и стирания строки были
спроектированы для работы в кольцевом режиме, ни одна строка не будет потеряна. Так как ото¬
бражаемый и скрытый буфера смежны, программа для реализации этого кольцевого буфера будет
весьма проста. Окончательное распределение памяти в редакторе, выбранное нами, включая допол¬
нительную строку для процедур копирования-замещения, показано в табл. 13.4.
174
весьма проста. Окончательное распределение памяти в редакторе, выбранное нами, включая допол¬
нительную строку для процедур копирования-замещения, показано в табл. 13.4.
Представляется полезным добавить кольцевой буфер к редактору, и это можно рассматривать в
качестве примера для модификации других редакторов, не имеющих таких возможностей. (Как это
работает, будет показано в деталях позднее.)
Этого обзора было бы достаточно, чтобы начать писать редактор, но мы добавили еще два огра¬
ничения. Мы знали, что надо свести число терминальных операций к минимуму, чтобы редактор
работал с наиболее широким спектром ЭВМ. После некоторого размышления мы решили спроекти¬
ровать редактор, который работает, используя только две терминальные команды: 1 - очистка экра¬
на и 2 - установка курсора на заданную строку и столбец. Было ли это практичным, стало ясным
после того, как редактор был частично написан, но это была цель, за которую мы боролись. Позд¬
нее мы ограничили нашу задачу написанием редактора для минимального размера экрана 16 строк
по 64 столбца, что упростило текст программы.
Это завершило спецификацию ’’высокого уровня” для редактора; теперь мы в основном поняли,
как должен работать редактор. Продолжать более общее описание функций редактора без реального
решения некоторых проблем ’’нижнего” уровня было бы в заметной мере пустой тратой времени.
Например, вдруг число слов управляющих терминалом, нельзя сократить до 2? Теперь мы были го¬
товы начинать писать Форт-программу. Для большинства языков программирования требуется
много, больше планирования, так как модульная структура, присущая словам Форта, им недоступ¬
на. Если вы изучаете Фортран, Кобол или большинство других языков, в ваших интересах сплани¬
ровать все как можно подробнее, прежде чем написать строчку программы. Например, вам надо вы¬
брать, какой управляющей клавише присвоить какую функцию, и вы должны мысленно спроекти¬
ровать все управляющие функции терминала. Процесс разработки при этом будет дольше и конеч¬
ный продукт, вероятно, не будет столь уж хорош. Форт позволяет писать и отлаживать программы
методом проб и ошибок или на интерактивной основе. Это выявляет многие ошибки прежде, чем
они получат шанс распространиться непредсказуемым путем через программу. Мы не будем описы¬
вать все пробы и ошибки, которые были совершены при написании редактора, но попытаемся пред¬
ставить вам идеи некоторых шагов, которые предпринимались.
Закладка фундамента
До сих пор мы строили замок грез, лишенный фундамента. Наши размышления привели к ясно¬
му пониманию того, какой редактор мы хотим создать, и к конкретным идеям построения некото¬
рых его структур высокого уровня. Но прежде чем мы сможем проверить какие-либо наши идеи,
мы должны определить слова, на которых будет базироваться все остальное. Должны быть описаны
переменные и константы (хотя некоторые из них могут быть добавлены позднее). Должны быть за¬
фиксированы некоторые адреса памяти. Нужно описать слово или слова, управляющие функциями
терминала. Должна быть решена проблема, как изменить и запомнить положение курсора на экра¬
не терминала и соответствующие позиции в буфере редактора. Эти и другие аспекты работы редак¬
тора должны быть рассмотрены на ранних этапах.
Хотя мы опишем окончательный текст программы блок за блоком (чтобы сделать ее более понят¬
ной), сам редактор пишется не так. Мы сначала написали редактор, который может делать очень
немногое, чтобы кое-что проверить. Базовые слова дописывались позднее в процессе написания про¬
граммы и были помещены в первые два блока, чтобы сохранить иерархию функций слов от наиме¬
нее к наиболее сложным и сделать программу простой для понимания. Мы группировали слова по
их функциям настолько, насколько возможно, в некоторых случаях проводя переукладку после то¬
го, как программа была функционально завершена.
Кто-то может сказать, что текст программы написан слишком плотно, но мы должны были эко¬
номить место в книге. Нужно оставить достаточно свободного места в программе, чтобы можно бы¬
ло выполнить переукладку слов согласно их функциям, а также чтобы осталось место для измене¬
ний и добавлений. Если вы делаете много изменений в тексте программы, вам нужно место для ее
расширения. Первые два блока редактора содержат описания констант, переменных, флагов и адре¬
сов некоторых буферов и базовые слова для управления курсором и терминалом.
Переменные ROW и COL (строка и столбец, где находится курсор) и I/R (флаг ввод-замещение)
были действительно описаны первыми. Остальные были добавлены по мере необходимости. Позднее
в процессе программирования с целью экономии памяти были описаны некоторые числа (BL, lK,
\15
64 и т.д.). PDELAY и SDELAY представляют собой два цикла, организующие задержки для отобра¬
жения информационных сообщений.
0 ( 20 июля 85 Экранный редактор NS 01 из 10)
1: TASK ; DECIMAL
2
3 32 CONSTANT BL 1024 CONSTANT 1K
4 64 CONSTANT 64 63 CONSTANT 63
5 10000 CONSTANT PDELAY 5000 CONSTANT SDELAY
6 VARIABLE SCR VARIABLE ROW
7 VARIABLE COL VARIABLE I/R
8 VARIABLE LOWBLK VARIABLE HIGHBLK
9 ( запоминание первого и последнего блоков, которые
можно редактировать на вашей ЭВМ)
10 1 LOWBLK ! 169 HIGHBLK !
Они являются константами, чтобы можно было изменить задержки, не изменяя текста программы.
Константы и переменные должны быть собраны в начале программы (или в начале секций большой
программы) для того, чтобы их было легко найти и изменить. Переменная SCR была описана для
тех версий Форта, где этого слова нет. Переменные LOWBLK и HIGHBLK были добавлены для то¬
го, чтобы редактор не имел доступа к тем блокам, к которым не следует. Они могут быть изменены
в тексте программы или с клавиатуры после того, как редактор загружен.
11 : ; ( слово, не выполняющее никакой pa
боты, для исполнительного вектора)
Это слово (названное так, чтобы бросаться в глаза в тексте программы) позволяет использовать не¬
описанные управляющие клавиши в исполнительном векторе KEYVECTORS (блок 9). Это слово
также было описано, когда был описан KEYVECTORS, но помещено здесь, так как это слово низ¬
кого уровня.
12 . M0DE (—) I/R @ 0=I/R ! , ( выбор ввод-замещение)
Каждый раз, когда используется MODE, оно переводит значение I/R из состояния истинно в состо¬
яние ложно или наоборот. MODE действует как переключатель, который выбирает режим ввода
символов.
13 : DELAY ( n — ) 0 D0 L00P ; ( Задержка на n циклов)
14 : PAUSE ( — ) PDELAU DELAYS ; ( Длинная задержка)
15 : SPAUSE ( — ) SDELAY DELAYS ; ( Короткая задержка)
Эти циклы задержки были описаны позднее, но перемещены в начало текста программы. PAUSE и
SPAUSE были описаны как отдельные слова, чтобы в дальнейшем сэкономить место в программе.
Основополагающие слова размещены в следующем блоке.
0 ( 20 июля 85 Экранный редактор NS 02 из 10)
1 : PADDR ( n — ) CREATE, D0ES> @ PAD +,
2 : 1K 2 * PADDR BLINE ( Последняя строка кольцевого
буфера)
3 1К 3 * 64 + PADDR LBUFF ( Адрес строки для копиро
вания-замещения)
Слово-описатель PADDR (”PAD ADDRESS” = адрес PAD) используется для формирования двух
адресов, необходимых при работе с буферами редактора. Для симметрии мы могли бы описать так¬
же 0 ADDR OLINE, чтобы получить адрес PAD (начало буфера редактора), но в тексте программы
использовался PAD для того, чтобы подчеркнуть, какой именно адрес памяти используется. Коман¬
ды lK 2 * и lK 2 * 64 + применены для вычисления значений 2048 и 2112, так как это легче по¬
нять в терминах блоков и строк, которые, в свою очередь, дают лучшбе представление о структуре
буфера редактора. Заметьте, что мы не заботимся о времени вычислений, так как они выполняются
только раз, во время компиляции.
4 : C0NTR0L ( с —) CREATE DEPTH DUP С, 0 D0 DEPTH R0LL С,
5 ' L00P D0ES> DUP DUP C@ + SWAP D0 I 1+ C@ EMIT L00P ;
6 27 42 C0NTR0L <CLR> ( Очистка экрана, курсор вверх влево)
CONTROL - слово-описатель, сформированное для описания слов, которые генерируют управля¬
ющие коды и ESC-последовательности. На терминале ADM 31 работает
27 42 C0NTR0L <CLR>
так как ESC-последовательность
ESC *
176
(ASCII 27 и последующий ASCII 42) используется для очистки экрана. Хотя CONTROL имеет
встроенное описание, это слово упрощает описание слов управления терминалом и делает текст
программы более читаемым. Описание
<CLR> 27 EMIT 42 EMIT ,
также будет работать, но применение слова-описателя CONTROL при наличии нескольких описа¬
ний сэкономит память. Слова, имеющие отношение к функциям терминала, снабжены именами, за¬
ключенными в треугольные скобки, чтобы они выделялись в тексте программы.
7 : <CXY> 27 EMIT 61 EMIT ROW @ 32 + EMIT COL @ 32 + EMIT ,
8 ( Положение курсора в строке X и столбце Y)
Это описание демонстрирует типовой образец слова, осуществляющего прямое позиционирование
курсора. Оно работает с ADM 31 и, конечно, будет переделано для других терминалов или ЭВМ.
9 ( : <CLR> PAGE; : <CXY> ROW @ COL @ PTC ,)
Это показывает, как можно описать <CLR> и <CXY>, применяя слова Форта, а не ESC-последова-
тельности (эти примеры используют слова MMSFORTH).
10 ( Чтобы использовать далее <CXY>, <HMC>, <DLF>, и <ADV>)
11 ( необходимо описать их здесь. Используется лишь в
12 случае отсутствия прямого перемещения курсора)
13 ( 28 CONTROL <HMC> (перемещение курсора вверх влево)
14 ( . <CXY> <HMC> ROW @ ?DUP IF 0 DO <DLF> LOOP THEN >
15 ( COL @ ?DUP IF 0 DO <ADV> LOOP THEN ,)
Требуется только одно описание <CXY> ("cursor-X-Y”) и <CLR> ("clear”); это альтернативное
описание <CXY> медленнее и основано на <DLF> (”downward-line-feed” = переход на одну строку
вниз) и <ADV> (’’advance” = сдвиг курсора на одну позицию), которые описываются оператором
CONTROL. Это слово следует использовать, только если терминал позволяет очень примитивное
управление курсором. Чтобы сделать программу легко читаемой, все слова, описанные CONTROL,
нужно сгруппировать вслед за описанием самого слова CONTROL.
В блоке 3 продолжается описание слов, управляющих курсором.
0 ( 20 июля 85 Экранный редактор NS 03 из 10)
1 @CURS0R ( — г с) ROW @ COL @ , ( Вызов строки и столбца)
2 : 'CURS0P ( r с — )C0L 1 R0W 1, ( Запоминание строки и столбца)
@CURSOR и !CORSOR были описаны, чтобы упростить вычисления и запоминание положения
курсора. Это было сделано после того, как мы выяснили, что комбинации, которые они представля¬
ют, появляются в нескольких местах текста разрабатываемой программы. Эти слова довольно часто
встречаются попарно в одном и том же описании и делают программу легко читаемой. Представле¬
ние положения курсора через номера колонок и столбцов (а не только через номера байта) упроща¬
ет перемещение курсора и описание некоторых последующих слов.
3 ( опишите здесь, если возможно, <STL> и <HMC> и ис
пользуйте в блоках 3 и 4)
4 : <S0L> 0 COL ! <CXY> ; ( Перемещение курсора в
начало строки)
5 : <H0M> 0 0 !CURS0R <CXY> , ( Перемещение курсора
вверх влево)
Слова <STL> (”start-line” - начало строки) и <HMC>
(”home-cursor” - курсор на место) могут быть описаны с помощью CONTROL (если ваш терминал
поддерживает эти функции) и использованы вместо <CXY> в этих определениях. Заметьте, что по¬
ложение курсора должно отслеживаться в двух местах: его реальное положение запоминается в
ROW и COL, но курсор должен быть также помещен в соответствующую точку на экране.
6 : BAB0VE ( —n ) ROW @ 64 * , ( Число байт в рядах
выше курсора)
7 : BBEL0W ( --n ) 16 ROW @ - 64 * 1K +; ( Число байт
ниже курсора)
8 : OFFSET ( —п ) BAB0VE COL @ +; ( Число байт в буфере
перед курсором)
9
Неудачно названные BABOVE (’’bytes-above” - число байтов до) и BBELOW C’bytes-below” -
число байтов после) - слова, которые пригодятся позднее. Они вычисляют число байтов (в буфере)
в строках выше курсора и число байтов (также в буфере) в строке, где находится курсор, а также в
строках ниже его (включая весь кольцевой буфер). Слово OFFSET кладет в стек число байтов, от¬
177
считанное от начального положения курсора (верхний левый угол). Так как это слово используется
только в CPOS, OFFSET является излишним, оно лишь делает описание CPOS более легким для
понимания. Обратите внимание, что любое изменение величины ROW или COL поменяет число,
выдаваемое в стек оператором OFFSET (и CPOS).
10 : CPOS ( — адр) PAD OFFSET +: ( адрес курсора)
11 : LSTART ( — адр) PAD BAB0VE +; ( адрес начала строки)
12 : LEND ( — адр) LSTART 63 +; ( адрес конца строки)
Слова CPOS (’’cursor-position” - положение курсора), LSTART
("line-start - начало строки) и LEND (”line-end” - конец строки) выдают три адреса в буфере, ко¬
торые широко используются в последующем тексте редактора.
14 BLEFT ( —n ) LEND CPOS - ; ( Число байтов в строке
слева от курсора)
15
BLEFT (”bytes-left” - байтов осталось) необходимо для последующих слов, которым нужна инфор¬
мация о числе байтов (символов) между курсором и концом строки.
Заметьте, что положение курсора в буфере задается несколькими способами. Мы знаем его стро¬
ку и столбец, число байтов до и после него в PAD-буфере, число байтов от начала буфера, его ад¬
рес в памяти, адрес начала и конца строки, где он находится, и число байтов, лежащих в строке
после курсора. Мы теперь имеем полный набор слов, позволяющий нам описать большое число слов
высокого уровня в блоке 4
0 ( 20 июля 85 Экранный редактор NS 04 из 10)
1 ( Если возможно, опишите <BSP>, <ADV>, <ULF> и <DLF> здесь
для того, чтобы использовать их)
2 ( вместо <CXY> в описаниях на)
3 ( строках 4, 5, 6 и 7 )
4 LEFT COL @ 0 > IF -1 COL +' <CXY> (или <BSP>) THEN ;
5 RIGHT COL @ 63 < IF 1 COL +! <CXY> (или <ADV>) THEN ;
6 . UP ROW @ 0 > IF -1 ROW +! <CXY> (или <ULF>) THEN ;
7 : DOWN ROW @ 15 < IF 1 ROW +! <CXY> (или <DLF>) THEN ;
Первая и наиболее важная вещь, которую мы хотели бы сделать, это двигать курсор в четырех
направлениях, но не покидая экрана. Слова <BSP> и т.д. должны быть описаны с помощью
CONTROL, если ваш терминал или ЭВМ позволяют это, и затем использованы вместо <CXY> в
этих словах. Заметьте, что ROW и COL должны так изменяться, чтобы отслеживать соответствие
положения курсора и адреса в буфере редактора.
8 . <EEL> (—) BLEFT SPACES <CXY> ; ( Стереть конец строки)
Было бы лучше описать оператор <EEL> (”erase-end-line” - стереть конец строки) с помощью опе¬
ратора CONTROL при условии, что ваш терминал позволяет это.
9 : NEWLINE ( — ) <S0L> DOWN ; ( Курсор в начало
следующей строки)
Имени NEWLINE будет поставлена в соответствие клавиша <BK> (которая формирует код, экви¬
валентный CTRL-M), чтобы установить курсор в начало следующей строки. Это слово имитирует
функцию ’’возврат каретки + перевод строки” (<BK>). Однако в редакторе BK только перемещает
курсор; это не должно вызывать перемещение текста на экране.
10 : SHOWLINES ( — ) @CURS0R 16 R0W @ ( отображение строк)
11 D0 I R0W ! <S0L> LSTART 64 -TRAILING TYPE <EEL> L00P
12 !CURSOR <CXY>;
13 : SH0WBLK ( — ) @CURS0R <H0M> SHOWLINES !CURS0R <CXY>;
Оператор SHOWLINES отображает строку, где находится курсор и все последующие строки до
конца блока. Когда изменена только нижняя часть экрана, это экономит время по сравнению с ото¬
бражением заново всего экрана. Оператор SHOWBLK испрльзует оператор SHOWLINES для ото-
боа^кения всего блока после перемещения курсора влевовверх. Обратите внимание на то, как
@CURSOR и !CURSOR используются в паре для занесения в стек кодов исходного положения кур¬
сора и для последующего в конце работы слова восстановления его позиции.
14 : TYPELINE ( — ) CP0S BLEFT 1 + 0VER 0VER TYPE -TRAILING
15 SWAP DR0P BLEFT 1+ -SPACES <CXY> ;
Оператор TYPELINE пропечатывает все символы, начиная от курсора и до конца строки. -
TRAILING используется для определения числа пробелов, необходимых для того, чтобы избавиться
от лишних символов, которые в противном случае появятся за пределами 64-символьной строки.
178
Мы имеем теперь к концу четвертого блока описания почти всех слов низкого уровня, в частно¬
сти слов, оперирующих адресами памяти, перемещающих курсор и отображающих текст на экране.
Только некоторые из действительно редактирующих команд были описаны в первых четырех бло¬
ках, но в следующих четырех блоках мы опишем остальные.
Упражнения
1. Почему строка 1 блока 1 записана следующим образом
TASK , DECIMAL
2. Почему лучше описать LOWBLK и HIGHBLK как переменные, а не как кОнстанты ?
3. Почему PDELAY и SDELAY описаны как константы, а не использованы непосредственно числа в описаниях PAUSE и
SPAUSE?
4. Приведите две причины для выделения DELAYS из описаний PAUSE и SPAYSE.
5. Предположим, что вы используете терминал ADM 31 и хотите добавить слова PAGE и РТС из MMSFORTH в ваш
Форт. Сформулируйте их описание.
6. Предположим, что вы работаете с терминалом, на котором работают ESC-последовательности:
27 13 Сместить курсор на одну строку вниз.
27 14 Сместить курсор вперед на один символ.
27 15 Сместить курсор вверх влево (НОМЕ).
Опишите <HMC>, <DLF> и <ADV> в строках 10, 11 и 12 блока 2. Что бы вы теперь сделали, чтобы использовать описание
<CXY>, приведенное в строках 14 и 15 ?
7. Почему модификации, предложенные в строках 1, 2 и 3 блока 4, были бы предпочтительнее, если они возможны?
Основные положения
Теперь, когда фундамент заложен, в следующих четырех блоках может быть описано большинст¬
во редактирующих команд. Эти операторы основаны большей частью на словах, которые мы уже
описали, а не на словах, описанных в Форте. Наибольшее удовольствие доставляет написание про¬
грамм Форта среднего и наивысшего уровней, так как вы при этом используете в основном язык,
созданный вами самими. Но на этой фазе проявляются противоречия и пропуски, допущенные в на¬
писанных ранее программах. Это как раз то время, когда вы оцените оставленное вами свободное
место в предшествующих блоках для расширения возможностей и внесения изменений. Пока мы
писали редактор, блоки 1 - 4 занимали на самом деле 7 блоков. Порядок, в котором описываются
слова промежуточного уровня, до некоторой степени произволен, поскольку большинство из них яв¬
ляются независимыми. Разумно помещать связанные слова в одном и том же блоке хотя бы для то¬
го, чтобы легче было их найти и отредактировать.
0 ( 20 июля 85 Экранный редактор NS 05 из 10)
1
2 : BCLEAR ( — ) PAD 1K + 1152 BL FILL ; ( Очистка буфера)
3 : SCLEAR ( — ) PAD 1K BL FILL SH0WBLK ; ( Очистка экрана)
4
Имеются слова для очистки содержимого кольцевого буфера и дисплея.
5
6 . L0ADBLK ( — ) SCR @ BLOCK PAD 1K CMOVE ; ( Загрузка блока)
7 RESTORE ( — ) L0ADBLK SH0WBLK ;
8
Слово LOADBLK (”load-block” - загрузить блок) производит загрузку в блочный буфер, а затем и
в буфер редактора. RESTORE загружает буфер редактора и отображает его содержимое. RESTORE
аннулирует редактирование, проведенное с момента, когда блок был последний раз отредактирован
и сохранен. Функция RESTORE оказалась полезной сама по себе при написании программ (она от¬
сутствовала в исходной спецификации) и оказалась практичной, так как блочный буфер не исполь¬
зуется непосредственно при редактировании.
9 : (UPDATE) ( — ) PAD SCR @ BLOCK 1K CMOVE UPDATE ; ( Пометка для сохранения)
Это слово копирует отображаемую часть буфера редактора в блочный буфер и помечает для со¬
хранения. Его имя в скобках предполагает, что оно тесно связано с функцией UPDATE, описанной
ниже.
179
10
11 . +BLK ( — ) SCR @ HIGHBLK @ < IF 1 SCR +! RESTORE THEN ;
12 : -BLK ( — ) SCR @ LOWBLK @ > IF -1 SCR +! RESTORE THEN ;
Эти слова используются для начала редактирования следующего или предшествовавшего блока.
Заметьте, что блок, который вы покидаете, должен быть помечен для сохранения (UPDATE), если
изменения, сделанные в нем, нужно записать на диске, прежде чем вы продолжите работу.
HIGHBLK и LOWBLK отсутствовали в исходной спецификации; они были добавлены позднее для
того, чтобы предотвратить доступ редактора к запретным или несуществующим.блокам.
13
14 : CLRT0P ( — r с) @CURS0R <H0M> <EEL>;
15 : WRITET0P (r с —) <H0M> TYPELINE 'CURS0R <CXY>;
Слова CLRTOP (”clear-top” - очистить верх) и WRITETOP были описаны для упрощения напи¬
сания подсказок, которые используются в четырех словах следующего блока. При совместном ис¬
пользовании они запоминают положение курсора в стеке, заполняют верхнюю строку на терминале
пробелами, восстанавливают верхнюю строку и устанавливают курсор туда, где он был.
Четыре слова в блоке 6 выдают информацию оператору и (или) обеспечивают выбор из меню.
0 ( 20 июля 85 Экранный редактор NS 06 из 10 )
1 : 9BLK# ( — ) CLRTOP. ” * * * BL0CK#: " SCR @. ( блок#)
2 PAUSE WRITETOP ;
?BLK# (’’block-number” - номер блока) отображает текущий номер блока на верхней строке,
ждет некоторое время, а затем восстанавливает верхнюю строку на дисплее. Это слово просто напо¬
минает оператору номер блока, который редактируется. Длительность паузы задается константой
PDELAY.
4 . UPDATES ( — ) (UPDATE) ( пометить блок и показать это)
5 CLRTOP .” * * * UPDATED Block#: ” SCR @ SPAUSE
WRITETOP ,
Оператор UPDATES позволяет вам пометить блок для сохранения и затем подтвердить эту опе¬
рацию, используя более короткую паузу, чем в ?BLK#. Отображение для этого необязательно, но
без него у вас не будет средств убедиться, что пометка (UPDATE) действительно произошла.
6
7 : 9CLEAR ( — ) CLRTOP
.” * * * X-0UT: (B)uffer, (S)creen ?”
8 KEY DUP 66 = IF DROP BCLEAR
9 ELSE 83 = IF SCLEAR !CURS0R <CXY> EXIT THEN
10 THEN WRITET0P;
Это и следующее слова представляют субменю, которое позволяет присвоить заданные функции
определенным управляющим клавишам в основной программе. В ?CLEAR (’’очистить?”) предпола¬
гается три варианта: очистить буфер, экран или ничего (ничего, если вы нажмете любую клавишу,
кроме ”В” или ”S”). Верхняя строка восстанавливается, если информация на экране не была стерта
(что показывает, насколько полезной может быть операция EXIT). В любом случае курсор будет
установлен туда, где он был ранее.
11 : ?ЕХ1Т ( f — ) CLRT0P ” * * * EXIT (S)ave, (Q)uit?"
12 KEY DUP 83 =
13 IF DR0P DR0P DR0P UPDATE FLUSH 1+ EXIT
14 ELSE 81 = IF DR0P DR0P EMPTY-BUFFERS 1+ EXIT THEN
15 THEN WRITET0P ;
Это слово, используемое для ухода из редактора, работает во многом так же, как и 7CLEAR,
представляя три варианта, прежде чем делать что-либо. Слово 1+ становится понятным, когда вы
заглянете в EDITLOOP в последнем блоке, оно устанавливает флаг, позволяющий выйти из редак¬
тора. EXIT используется здесь так же, как и 7CLEAR, для предотвращения в WRITETOP восста¬
новления верхней строки при выходе из редактора.
Следующий блок содержит слова, необходимые для ввода текста и изменения его различными
путями. Существуют два основных способа или режима, используемые для ввода символов: замеще¬
ние символа, на который указывает курсор, или раздвижка текста
(перемещение остальной части строки на одну позицию вправо) перед вводом нового символа.
0 ( 20 июля 85 Экранный редактор NS 07 из 10)
1 : 0PENUP ( — ) ( Раздвинуть текст в месте буфера, куда указывает курсор)
2 C0L @ 64 < IF CL0S DUP
180
3 1+ BLEFT <CMOVE ( или CMOVE>) BL CPOS С! THEN ;
4 : OPEN ( — ) OPENUP TYPELINE ; ( Раздвинуть текст в месте, указанном курсором)
OPENUP необходимо для ввода пробела в позицию, указанную курсором, перед вводом символа.
Это слово воздействует только на буфер, ничего не отображая на экране. Если вы не хотите терять
конец строки, который выдвигается в правую сторону дисплея, проверяйте последний символ в
строке (используя LEND @), чтобы убедиться, что это пробел, прежде чем позволить слову
OPENUP делать чтобы-то ни было. Слово OPEN позволяет вам вводить пробел в позицию, отме¬
ченную курсором, смещая текст на один символ вправо. Это побочный продукт слова OPENUP, ко¬
торое все равно нужно было написать для INSERT.
6 TRUNC ( --) <EEL> CPOS BLEFT BL FILL ; ( Укоротить
строку)
TRUNC (’’отбросить”) заполняет пробелами часть строки, лежащую между курсором и концом
строки.
7
8 : OVERTYPE ( симв — ) COL @ 64 < ( Заместить символ, на
который указывает курсор)
9 IF DUP EMIT CPOS С! 1 COL + ! ELSE DROP THEN ;
10
Это основное слово для замещения символа, на который указывает курсор, символом, лежащим в
стеке. Если курсор не находится за краем строки, символ, лежащий в стеке, отображается на тер¬
минале и записывается в соответствующем месте буфера редактора. Положение курсора в буфере
(COL) увеличивается на 1, чтобы обеспечить соответствие с положением терминального курсора,
который при отображении символа перемещается на одну позицию вправо.
11 : INSERT ( симв — ) OPENUP OVERTYPE TYPELINE ; ( Ввести
символ )
Слово INSERT сначала раздвигает текст, затем замещает пробел, возникший на месте курсора,
символом, лежащим в стеке, после чего отображает строку.
12 : DELETE ( — ) COL @ 64 < ( Стереть символ)
13 IF CPOS 1+ CPOS BLEFT CMOVE BL LEND C'
14 <EEL> TYPELINE
15 THEN ;
Стирание символа выполняется путем перемещений текста между курсором и концом строки (в
буфере редактора) на одну позицию влево, записи пробела в конец строки (в буфере), стирания
строки на экране и отображения измененной строки.
Следующий блок работает с невидимыми на экране буферами (строчный и кольцевой буферы).
Строчный буфер используется для получения копии строки, которая заменит строку где-либо еще.
Он особенно полезен для запоминания строки заголовка, которая будет использоваться в несколь¬
ких блоках. Кольцевой буфер воспринимает одну или более строк, которые были стерты. Эти стро¬
ки затем могут быть вставлены где-либо еще в том же или другом блоке. Если вы хотите избавить¬
ся от некоторых строк совсем, то укоротите их с помощью оператора TRUNC при положении курсо¬
ра на левом поле, прежде чем стирать и помещать их в кольцевой буфер. Слово BCLEAR очистит
все строки в кольцевом буфере (см. его описание в блоке 5). To, что вы видите на экране, если
блок помечен (UPDATE), то и будет записано на диск.
0 ( 20 июля Экранный редактор NS 08 из 10)
1 • CLINE ( — ) LSTART LBUFF 64 CMOVE , ( Копирование
строки в буфер)
2 •
3 : PLINE ( — ) LBUFF LSTART 64 CMOVE ( Извлечь строку из
буфера )
4 CURSOR <S0L> TYPELINE !CURS0R <CXY> ,
CLINE (”copy-line” - скопировать строку) и PUNE (”put-line” - вставить строку) работают с бу¬
фером строки (по адресу LBUFF), предназначенным для копирования и замещения отдельной стро¬
ки. Полезной модификацией редактора было бы постоянное отображение содержимого строчного бу¬
фера сразу под текстом блока на экране, если ваш экран имеет достаточно места.
5
6 : KLINE ( — ) ( Стереть строку, занеся ее в кольцевой
буфер)
7 LSTART BLINE 64 CMOVE ( Перенос текущей cfpoKn)
181
8 LSTART 64 + LSTART BBELOW CMOVE ( Сдвинуть буфер вверх)
9 SHOWLINES ; ( Отображение измененных строк)
Слово KLINE (”kill-line” - стиреть строку) стирает строку, на которую указывает курсор, из ото¬
бражаемой части буфера редактора и переносит ее в кольцевой буфер по адресу BLINE. Затем весь
буфер редактора, начиная со строки ниже курсора и кончая BLINE, сдвигается на одну строку (64
символа), так что перекрывает стертую строку. Слово SHOWLINES отображает строки, начиная с
помеченной курсором до конца экрана. Последней строкой экрана станет верхняя строка кольцевого
буфера, в котором строки при работе перемещаются по ротационной схеме.
11 : I LINE ( —) ( Ввести строку из кольцевого буфера)
12 LSTART DUP 64 + BBELOW <CMOVE ( или CMOVE>) ( сдвинуть
буфер)
13 BLINE LSTART 64 CMOVE ( Перенести строку с низа бу
фера)
14 SHOWLINES ; ( Отображение измененных строк)
Слово ILINE (”insert-line” - ввести стоку) сдвигает кольцевой буфер на одну позицию. Весь бу¬
фер редактора, начиная со строки с курсором и кончая строкой перед BLINE, смещаются вниз на
64 символа, а последняя строка (теперь по адресу BLINE) переносится, чтобы заместить строку, где
был раньше курсор; изменения отображаются оператором SHOWLINES. Это выглядит как враще¬
ние кольцевого буфера вниз на одну строку. Здесь описаны все функции, используемые редактором.
Остается только соединить эти функции вместе, чтобы редактор выполнял то, что было вначале за¬
думано.
Соединение частей в единое целое
Наиболее интересная фаза написания любой программы - это внесение последних поправок и на¬
блюдение за тем, как она, наконец, работает. В Форте последняя фаза доставляет даже большое
удовольствие, так как вы проверяете слова промежуточного уровня и теперь видите, как они собра¬
ны и как работают совместно. Если вы хорошо поставили задачу и выразительно назвали ваши сло¬
ва, сборка всего вместе - обычно наиболее простая часть написания программы на Форте.
Простейший способ доступа ко всем 23 командам редактора - это создать исполнительный вектор
для командных слов. Если используется клавиша CTRL в комбинации с буквой от ”А” до ”Z”, то
это дает 26 возможных команд. Слово — было создано в блоке 1, чтобы поставить в соответствие
неиспользуемым управляющим символам пустое слово, не выполняющее никакой работы.
0 ( 20 июля 85 Экранный редактор NS 09 из 10)
1 CREATE KEYVECTORS ] ( Исполнительный вектор
команд редактора)
2 LEFT ( А Курсор влево ) ?BLK# ( В Номер блока )
3 CLINE ( С Копирует строку ) DELETE ( D Стирает символ )
4 ?EXIT ( E Уход из редактора ) ( F )
5 MODE ( G Переход в новый режим )—( H )
6 I LINE ( I Ввод строки ) ( J )
7 KLINE ( К Стирание строки ) -BLK ( L Последний блок )
8 NEWLINE ( M или ENTER, CR+LF ) +BLK ( N Следующий блок )
9 OPEN ( 0 Раздвинуть текст ) PLINE ( P Вставить строку )
10 <H0M> ( Q Курсор на место ) RESTORE ( R Восстановить
экран )
11 RIGHT ( S Курсор вправо ) TRUNC ( T Укоротить строку )
12 UPDATES ( U Поместить буфер ) ( V )
13 UP ( W Курсор вверх ) 7CLEAR ( X Очистить буфер/экран )
14 ( Y ) DOWN ( Z Курсор вниз )[
Дополнительные приложения слов ] и [ описаны в гл.15, но здесь достаточно знать, что они по¬
мещают адреса слов, записанных между ними, в тело описания KEYVECTORS, как это требуется
для исполнительных векторов. Функции, присваиваемые клавишам, произвольны, и мы действи¬
тельно меняли местами команды в процессе написания программы; вы можете делать то же самое.
Оператор KEYVECTORS может быть написан так, чтобы занимать намного меньше места на экра¬
не. Но тогда для документирования функций клавиш потребовалась бы отдельная таблица команд;
более эффективно было бы позволить описанию слова документировать само себя.
182
15 : KEYDO ( n —) 1- 2* KEYVECTORS + @ EXECUTE ;
Слово KEYDO воспринимает число (от 1 до 26, от CTRL-A до CTRL-Z) и исполняет n-ю коман¬
ду в KEYVECTORS. Теперь любая редактирующая команда может быть использована нажатием од¬
ной клавиши с пульта. Блок 10 как раз то место, где все соединяется в единое целое.
0 ( 20 июля 85 Экранный редактор NS 10 из 10)
1 . EDITCASE ( флаг симв — флаг’)
2 : DUP 27 < OVER 0 > AND ( Легальный управляющий символ
?)
3 IF KEYDO ( Если так, то исполняем ко
манду)
4 ELSE DUP 31 > 0VER 127 <AND ( Если нет, печатный
символ?)
5 IF I/R @ ( Если да, смотрим, какой режим)
6 IF INSERT ELSE OVERTYPE THEN ( и вводим или замещаем)
7 ELSE DROP ( Но, если символ непечатный, игно
рируем)
8 THEN
9 THEN ;
10
EDITCASE - ключевое слово редактора; это место, где анализируется то, что вводится с клавиа¬
туры, и производятся соответствующие действия. Слово EDITCASE предполагает наличие флага и
кода символа в стеке. Сначала анализируется символ и проверяется, лежит ли он в интервале меж¬
ду CTRL-A и CTRL-Z включительно, и если да, то выполняется соответствующая команда. Если
это не управляющий символ, его код проверяется еще раз, при этом выясняется возможность ото¬
бражения через ASCII-код. Если это так, то в зависимости от статуса ввод-замещение производится
одно из двух: символ либо вставляется в текст, либо впечатывается поверх того, который был в бу¬
фере редактора. Если символ непечатный, он игнорируется. Флаг в EDITCASE служит для управ¬
ления выходом из редактора. Флаг (0) заносится в стек словом EDITLOOP (описанным ниже) и за¬
меняется на 1, если в ?ЕХ1Т (в блоке 6) был сделан выбор ”exit” (выход). Слово может быть
заменено как здесь, так и в KEYVECTORS словом, описанным с помощью CONTROL, чтобы вы¬
дать звуковой сигнал, если ваша ЭВМ это позволяет.
Слово EDITCASE суммирует основные операции редактора и является Форт-интерпретацией час¬
ти, связанной со спецификацией клавиш, приведенной в первой части этой главы. Слово ts
EDITCASE может быть записано так, чтобы читаться почти так же легко, как словесное описа¬
ние, если бы оно было написано с использованием трех слов, описанных для того, чтобы исключить
три предложения, как это видно из нижеприведенного примера.
ПсевдоФорт из табл.13.2
Действительный текст
программы
EDIT ( п )
BL0CK-T0-BUFFER BL0CK-T0-SCREEN
CURS0R-T0-START
BEGIN
FETCH-CHARACTER C0NTR0L-CHAR?
: EDITCASE ( флаг симв—флаг’)
DUP C0NTR0L-CHAR?
IF KEYD0
IF KEYD0
ELSE PRINTABLE-CHAR?
ELSE DUP PRINTABLE-CHAR?
IF INSERT-M0DE?
IF INSERT-M0DE?
IF INSERT
IF INSERT
ELSE 0VERTYPE
ELSE 0VERTYPE
THEN
THEN
ELSE DR0P
ELSE DR0P
THEN
THEN
THEN
THEN;
END-FLAG UNTIL ;
Обратите внимание на сходство между EDITCASE и псевдоФорт-спецификацией для редактора в
табл.13.2. Все части оказались взаимно согласованными.
Хотя EDITCASE содержит базовую логику редактора, как показано в табл.13.2, нужно еще много
сделать, чтобы редактор был вполне функционирующим. Блок, который нужно отредактировать,
183
должен быть загружен в буфер редактора и должен быть запущен бесконечный цикл приема кодов
от клавиатуры, которые нужны для работы EDITCASE.
11 : EDINIT ( blk —) SCR ! EMPTY-BUFFERS LOADBLK SHOWBLK ;
Слово EDINIT C’edit-initialize” - вход в редактирование)
запоминает номер блока в SCR, очищает блочный буфер с помощью EMPTY-BUFFERS, загружает
содержимое блока в буфер редактора и отображает его на экране терминала.
12 : EDITLOOP ( — ) EDINIT 0 BEGIN KEY f_DI TCASE DUP UNTIL
13 DROP ;
Слово EDITLOOP запускает редактор с помощью EDINIT и помещает флаг 0 в стек для того,
чтобы редактор оставался в бесконечном цикле, пока флаг не будет заменен на 1 в ?ЕХ1Т (что за¬
ставит UNTIL прервать цикл), после чего этот флаг убирается.
14 : EDIT ( blk —) <HOM> <CLR> EDITLOOP <HOM> <CLR>,
15 : E SCR @ EDIT;
Описание закончено. Чтобы использовать редактор, следует напечатать слова EDIT и E. EDIT уста¬
навливает курсор в исходное положение, очищает экран и входит в EDITLOOP с номером блока в
стеке, по завершении редактирования экран вновь очищается. (Если вы хотите, измените EDIT так,
чтобы курсор появился под текстом блока, редактирование которого вы прервали по команде QUIT,
без стирания экрана.) Слово E представляет удобный способ возврата к работе с только что редак¬
тированным блоком, чей номер хранится в SCR. Если SCR - часть вашей Форт-системы, слово E
перейдет к редактированию блока, для которого последней выполнялась команда LIST. Редактор
завершен, если вы, конечно, не хотите его модифицировать. В гл.14 мы увидим,как для редактора
можно организовать отдельный словарь, но это сейчас необязательно. Даже если вы найдете этот
редактор проще того, который имеется в вашем Форте, наш детальный анализ может помочь вам
понять его и, может быть, даже улучшить.
Но наше основное соображение, почему мы дали столь детальное описание редактора, было не
просто документирование программы или оказание помощи для модификации вашего собственного
редактора: мы надеемся, что вы научились разрабатывать и писать сложные программы. Этот про¬
цесс обсуждается в следующем разделе.
Упражнение
1. Как бы вы отлаживали слова, описанные в блоках 9 и 10?
2. Мы дали два описания EDITCASE, последнее требует описания слов CONTROL-CHAR?, PRINTABLE-CHAR? и
INSERT-MODE?. Определите эти слова. Этот процесс выделения части программы и развертывания ее в виде отдельных
слов называется факторизацией описания. Подробнее вы прочтете об этом в следующем разделе.
Комментарии
Теперь, когда вы знакомы с текстом редактора и некоторыми причинами того, что она написана
именно так, посмотрим на процесс программирования с более общих позиций. Наш первый шаг за¬
ключается в формировании четкой идеи основных функций редактора. Это может быть сделано
многими путями: с помощью функциональной схемы (рис.13.1), словесного описания (табл.13.1) и
псевдоФорт- программы (табл.13.2). Для простой программы эти планы можно было бы держать в
голове. Но план нужен для того, чтобы направлять усилия при программировании.
Мы не можем чрезмерно подчеркивать важность процесса планирования. Соблазн сесть и напи¬
сать программу немедленно очень силен, особенно если пользуетесь языком Форт. Лучше обдумать
проблему в течение часов, дней или иногда недель. Если вам не терпится сесть за терминал, при¬
мите решение по части проекта, которая вам ясна, и приступайте к работе над ней. Например, мы
упражняемся с описаниями CONTROL, чтобы проще описать ESC-последовательность. To же самое
было сделано, когда мы присваивали функции клавишам в KEYVECTORS. Упражнение с частью
программы может быть очень полезным, например, для прояснения общего плана.
Упражняясь с частью программы, вы часто обнаруживаете, что вам нужно отладить слово, кото¬
рое использует еще неописанные слова. В этом случае полезно использовать слова, называемые
подставками. Подставки не выполняют никакой работы, но сообщают вам, что слово исполнено.
Например, KEYVECTORS может быть проверено путем замены слов между ] и [ на CRTLA,
CTRLB и т.д. до CTRLZ. CTRLA будет подставкой:
: CTRLA ." Control А typed” ;
184
KEYVECTORS и KEYDO и даже примитивная версия EDITCASE и EDITLOOP могут быть таким
образом протестированы. Такого рода упражнения могут быть весьма полезны при формировании
вашего плана. Мы выполнили это отчасти еще до начала программирования и еще больше в про¬
цессе написания редактора. Так как наши основные планы были сформулированы, мы приступили
к написанию настоящей программы.
Мы сначала написали редактор с минимальным числом функций для того, чтобы иметь базовую
версию, в пределах которой не могли отлаживать новые команды. Таким образом, EDITCASE и
EDITLOOP были написаны прежде, чем многие более ранние слова^. По мере формирования редак¬
тора связанные слова группировались в некоторых блоках, а слова низкого уровня были помещены
в первую пару блоков, если необходимость в них становилась очевидной. Когда редактор начал ра¬
ботать, были добавлены и отлажены некоторые новые команды. Процедура программирования со¬
стояла из создания слов низкого уровня, от которых все зависит, слов высокого уровня для обеспе¬
чения тестовых задач, слов среднего уровня для большинства редактирующих команд (добавления
слов низкого уровня по мере необходимости), и в заключение проводились отладка и доводка всего
в целом.
Благодаря возможностям, которые предоставляются в сфере тестирования и модификации, Форт
идеально подходит для внедрения новых идей в работающие программы. Ключом решения пробле¬
мы является разработка базового набора слов. Другими словами, следует разработать удобную но¬
менклатуру слов. Это ключевая концепция программирования на Форте. Номенклатура - это ”сис-
тема наименований, используемых в конкретной области знаний или искусства какой-либо шкрлой
или личностью, в частности названий, служащих при классификации для достижения различия с
другими техническими терминами (Webster’s New Collegiate Dictionary G. & С. Merriam Co.,
Springfield, Mass., 1959). Спецификация программ-первый шаг в процессе конкретизации проблемы.
Следующий шаг - сокращение спецификации до номенклатуры, приемлемой для данной задачи. Ес¬
ли номенклатура зафиксирована, тогда известны слова среднего уровня и остается написать про¬
грамму, используя эту номенклатуру.
Этот процесс разделения проблемы на более мелкие, которые решить легче, называется факто¬
ризацией. Хотя факторизация могла бы быть выполнена на любом универсальном языке ЭВМ с тем,
чтобы расщепить проблему на части, с которыми можно работать на этом языке, эта процедура осо¬
бенно проста и интуитивна на Форте. В Бейсике, Паскале и большинстве других языков факториза¬
ция состоит в разработке подпрограмм или процедур, которые вызываются при необходимости. Но в
этих языках (в особенности в Бейсике) факторизация часто очень трудна для начинающих. В
Форте подход к решению проблем является более интуитивным (и в действительности диктуется са¬
мим языком), так как каждое слово Форта является фактором. Каждое слово - это часть номенк¬
латуры, которая разработана в процессе решения проблемы и имеет большое значение в описании
решения. В идеале каждое слово воплощает лишь одну идею, так что его использование при постро¬
ении более сложных концепций в последующих словах довольно легко понять. Слова высокого уров¬
ня, хорошо написанной Форт-программы должны читаться почти так же, как словесное описание
проблемы. В действительности программа Форта в заметной мере самодокументируется. (Вы видели
это в упражнении 2 предшествующего набора задач.)
Существуют различные пути, какими можно факторизовать задачу. В редакторе позиция курсора
может быть выражена через число байтов от начала буфера или номера строчки и столбца. Эти два
метода представления положения курсора эквивалентны потому, что номера строки и столбца могут
быть вычислены из номера байта с помощью операции 64/MOD. Так какой же способ предпочти¬
тельнее? Имеются два соображения; положение курсора легче воспринять, зная строку и столбец,
да и программировать так легче: строка и столбец используются независимо более часто, чем номер
байта в буфере (который можно легко вычислить с помощью ROW и COL). Часто способ, которым
произведена факторизациия, сильно влияет на то, как пишется остальная программа. Хотя имена
слов в стандартном Форте могут быть весьма информативны (до 31 символа), длина немногих из
них достигает даже трети от этой величины. Почему? По одной причине: длинные имена долго пе¬
чатать. Может быть, более важно, что они занимают больше место на диске, которого всегда не
хватает. Часто сокращения - наилучший путь сохранить информативность имени. Слова базового
Форта (такие как @ и !) могут использоваться в именах слов, обозначая извлечение и запоминание
чисел (как в @CURSOR и !CURSOR). Если слово заносит флаг, включает в себя процедуру выбора
или ответа на вопрос, его имя может содержать ”?” (как ?BLK# и ?ЕХ1Т). И конечно, может ис¬
пользоваться любое число индивидуальных сокращений. Очевидно, баланс должен лежать между
чрезмерно длинными словами - описателями и короткими крайне непонятными. Ясно, также, что
все сокращения и личные системы кодирования имен слов должны применяться согласованно, что¬
185
бы иметь какую-то ценность. Хорошим тестом является проверка можете ли вы читать имена слов
вслух, что скажет вам, разумны ли ваши сокращения и, таким образом, будут ли они понятны спу¬
стя какое-то время. Другой полезной идеёй является присвоение словам имен, которые говорят о
том, что они делают, а не как. Важно рассматривать слова Форта как концепцию и часть решения
проблемы, а не как часть программы, которая что-то делает. Слова Форта часто проще назвать, ес¬
ли вы отслеживаете их функцию в контексте всей программы. Тезаурус, кстати, может быть таким
же ценным инструментом в Форте, каким он является при письме.
Комментарии идут ”рука об руку” с хорошими именами слов и имеют целью сделать ваш текст
программы понятным как при написании, так и в дальнейшем. Комментарии и состояние стека сле¬
дует вводить щедро в процессе программирования, чтобы вы могли отслеживать то, что вы ожидае¬
те от ваших слов. Лучше всего вычислить и записать эволюцию стека в слове сразу при его описа¬
нии, даже если никакого изменения стека не происходит. Эта привычка может весьма упростить за¬
дачу ознакомления с вашими новыми словами, так как вы не должны каждый раз просматривать их
описания, когда вы их используете. Конечно, после того, как программа написана, вы должны по¬
тратить столько времени, сколько нужно на тщательное оформление вашей программы и блоков,
чтобы они были как можно более читаемыми.
Другой полезной привычкой является использование первой строки каждого блока для информа¬
ции о содержании блока. Информация, обычно включаемая в эту индексную строку, содержит дату
последней модификации, заголовок программы, инициалы автора, относительный номер блока и об¬
щее число блоков в программе. Некоторые программисты любят перечислять в индексной строке
имена слов, описанных в блоке, чтобы облегчить поиск описаний. Порядок размещения этих дан¬
ных не играет роли, если использованная очередность позволяет выделить важную информацию.
Выводы
Намного легче ремонтировать автомобиль, который на ходу, чем тот, который даже не поставлен
на колеса. To же справедливо и для программирования на Форте: как только программа готова,
чтобы что-то делать, появляются идеи по ее улучшению и совершенствованию. Если окончательная
отладка программы доставляет удовольствие, то полная переделка, когда вы думали, что почти все
завершено, - вряд ли. Единственный путь избежать больших задержек при программировании - по¬
пытаться предвидеть трудности до того, как они случились. Это делается путем формирования яс¬
ной идеи относительно проблемы в целом, прежде чем писать программу.
В любом случае вы должны иметь ясное представление о вашей задаче и целях, прежде чем вы
приступите к программированию (на любом языке). Программирование на Форте более творческое,
интерактивное и итеративное, чем на других языках. Чарльз Мур создал Форт для своих собствен¬
ных приложений с целью увеличения личной производительности как программиста. Мы надеемся,
что эта глава прояснила, почему Форт увеличивает производительность. Форт называют усилите¬
лем идей. Существует несколько причин этого. Расширяемость Форта предлагает вам большой вы¬
бор. Применение слов Форта разделяет проблему на части, стимулируя логическое мышление. Ин¬
терактивная природа Форта способствует быстрой проверке, позволяет вам оттачивать ваши идеи. А
использование в Форте длинных имен, аналогичных словам естественного языка, позволяет писать
легко читаемые программы. Наконец, Форт дает вам больше власти над ЭВМ, чем может дать ка¬
кой-либо другой язык. Но было также сказано, что Форт может сделать хорошего программиста ве¬
ликим, а плохого - ужасным. Если вы не будете дисциплинированны в определении вашей задачи,
в факторизации и субфакторизации проблем, в определении слов с функциональными именами, в
написании хороших комментариев и в расположении вашей программы и блоков разумным обра¬
зом, вы попадете в категорию ужасных. Надеемся, что эта глава поможет вам двигаться в другом
направлении.
Глава 14
ПАМЯТЬ ФОРТА, СЛОВАРИ И КОНТЕКСТНЫЕ СЛОВАРИ
Теперь мы знаем, что Форт предоставляет вам большую власть над ЭВМ, чем большинство других языков. Целью данной
главы и двух последующих является предоставление вам возможности еще большего контроля. Форт обеспечивает значитель¬
ную гибкость там, где это возможно, за счет создания новых слов-описателей или, например, написания редактора. Но для
использования всех преимуществ Форта, его мощи и максимальной гибкости вы должны иметь хорошее понимание внутрен¬
него устройства языка. Вы должны понять, например, как Форт использует память ЭВМ, как устроен словарь и как он рабо¬
тает. Мы раскроем эти темы в данной главе. Вы должны также понять, как Форт интерпретирует входной поток информа¬
ции, как он интерпретирует слова и как происходит исполнение слов. Это темы гл.15. Наконец, для достижения полного
контроля над ЭВМ некоторые вещи должны выполняться на ассемблере. Мы обсуждаем Форт-ассемблер в гл.16.
Теперь вы уже сформировавшийся программист и можете писать сложные программы на Форте, но внутреннее устройст¬
во языка является для вас, вероятно, таинственным. Эти три главы раскроют тайну. Фактически мы расскажем вам кое-что,
что действительно необходимо знать, чтобы создать версию Форта. Существует несколько тем, такие как метакомпиляторы и
целевые компиляторы, декомпиляторы и дисассемблеры, использование в операционных системах, связь с аппаратурой и
различные пути реализации цепных программ ("шитых кодов”), которые мы не рассмотрим здесь, — это тема другой книги.
Но когда вы закончите последние три главы, у вас будет все, что нужно практически для любых приложений языка.
Об использовании памяти в Форте
Мы немало написали о словаре, дтеке, словах PAD, HERE, блочных буферах и других частях
Форта безотносительно к их действительному положению в памяти. Хотя детальное знание распре¬
деления памяти в Форте не нужно для использования языка, полезно иметь карту памяти, чтобы
сделать наглядным то, как организован Форт. Карта памяти (табл.14.1) является последовательным
списком позиций в памяти и их функций. Одни адреса показаны с их Форт-именами, в то время
как другие отмечают границы областей, используемых для
Таблица 14.1. Типичная карта памяти (MMSFORTH)1
Адрес
Шестнад¬
цатерич¬
ный
Деся¬
тичный
Функция
0
0
BLOCK ; блочный буфер 1
402
1026
BLOCK ; блочный буфер 2
44E
1102
TIB , текстовый входной буфер
Недокументировано Различные системные величины
и программы
804
2052
’FORTH; начало словаря, ядро
программ
в машинных кодах
9C4
2500
Словарь, исходные тексты не
поставляются
2008
8200
'HEX; Словарь, исходные тексты
поставляются
4A38
19000
Словарь, скомпилированные
программы пользователя
Разное
HERE, DP @, верх словаря, место
для приема данных от W0RD
Разное
PAD , временный буфер
Свободная память (переменного
размера)
Разное
SP@ или 'S; верх стека параметров
79E0
31200
S0 @ или S0; начало стека па
раметров
Разное
Верх стека возвратов
Разное
Начало стека возвратов
Разное
Начало дополнительного
блочного буфера
7D00
32000
Конец дополнительного блочного
буфера
1 Адреса памяти неточны, правильные значения смотритс в вашем руководстве по программированию.
187
других целей. Таблица 14.1 представляет собой карту распределения для конкретной реализации
MMSFORTH; карта вашей системы будет другой. Она может отличаться только специфическими
адресами или может быть фундаментально.другой, но описание, использующее MMSFORTH в каче¬
стве примера, поможет вам понять функциональные элементы почти любого Форта.
Если карта памяти не поставлена вместе с документацией вашей версии Форта, вы можете под¬
готовить ее сами на примере таблицы 14.1. Начните с пометки первой и последней ячеек памяти
вашей ЭВМ вверху и внизу листа бумаги и, заполнив столько позиций, сколько вы сможете, ис¬
пользуйте слова Форта, как в нашей модели, или их эквиваленты из вашего Форта. Вы можете вы¬
полнить некоторое исследование и изучить вашу документацию, и, если только вы не имеете вер¬
сию Форта с очень необычной организацией (HS/FORTH, например), вы сможете все разрисовать.
Хотя карты памяти и отличаются» благодаря особенностям работы Форта должны быть и сходст¬
ва. Все версии Форта должны иметь блочные буферы, программы в машинных кодах, словарь, сво¬
бодную память, а также стек параметров и стек возвратов; они составляют основу конструкции
языка. Но стандарты определяют только способ поведения Форта, а нё то, как это поведение реали¬
зуется. Таким образом реализации отличаются. Многозадачные и многопользовательские системы
будут особенно различными, и мы не будем здесь их обсуждать. Несмотря на это, последующее об¬
суждение карты для MMSFORTH применимо к большинству других версий Форта, даже если конк¬
ретные адреса памяти и будут отличаться.
В MMSFORTH область младших адресов памяти содержит два блочных буфера, сразу за ними
размещен текстовый входной буфер. Многие версии имеют блочные буферы в верхней части памя¬
ти. Эти адреса вы можете найти с помощью слов BLOCK и TIB. Вслед за входным буфером, но до
словаря, лежит область системных величин и программ в машинных кодах, которые имеют отноше¬
ние к функциям примитивов языка. Сюда относятся данные о числе и типах дисковых драйверов,
значения по умолчанию и текущие значения системных переменных, таких как указатель стека
возвратов, а также программы в машинных кодах, используемые всеми словами Форта. Значения
переменных пользователя (такие как BASE, STATE и BLK) также запоминаются в массиве перед
словарем, указания на их адреса обеспечиваются соответствующими словами. Это позволяет уста¬
новить все переменные пользователя (например, при инициализации системы) простой засылкой
таблицы в память.
Первым словом словаря является FORTH, которое представляет собой действительное имя кон¬
текстного словаря Форта (об этом подробнее в следующей главе). Словарь, конечно, включает в се¬
бя все слова Форта и их откомпилированные описания. Младшая часть словаря в MMSFORTH име¬
ет предкомпилированную форму, загружается непосредственно с системного диска и не может быть
легко изменена пользователем. Эта младшая часть состоит из описаний системных слов и собствен¬
ных программ, что отличает MMSFORTH от других версий. HEX — первое слово MMSFORTH,
текст которого доступен для программиста. Тексты Форт-описаний,начиная с HEX и далее, хра¬
нятся в виде блоков на диске и могут компилироваться по выбору или модифицироваться с целью
получения версии MMSFORTH, отвечающей вашим собственным требованиям. Этот текст включает
в себя расширение компилятора, ассемблер, программы управления печатающим устройствам, эк¬
ранный редактор и многое другое. Когда первая часть этого ’’репертуара” блоков загружена,
MMSFORTH становится согласованным со стандартом Форт-79. Некоторые версии Форта предостав¬
ляют текст почти всего словаря, для других это предоставляется за дополнительную плату. Некото¬
рые используют метакомпилятор для трансляции ядра словаря, а некоторые как MMSFORTH, рас¬
сматривают ядро словаря как собственность. Не важно, каков размер словаря, адрес первого доступ¬
ного байта после словаря засылается в стек оператором HERE. HERE берет значение указателя
словаря (переменная пользователя с именем DP в MMSFORTH и большинстве других версий, но ни
в одном из стандартов она не упомянута) и заносит его в стек. Слово HERE можно описать как
: HERE DP @ ;
В целях ускорения его можно описать в машинных кодах. Хотя мы часто обращаемся к HERE
так, как если бы это была константа, которая засылает в стек адрес конца словаря, нужно помнить,
что это в действительности слово, которое выдает значение переменной^юльзователя. To есть вы не
можете ее изменить, дав команду HERE !. Конечно, значение, выдаваемое HERE, изменяется по
мере пополнения словаря при компиляции новых слов или в результате работы оператора FORGET,
удаляющего слова из словаря. Действительно,
: ALLOT DP + ! ,
является описанием, используемым в некоторых версиях Форта для изменения значения HERE.
Вспомним из гл.9, что память, начиная с HERE и далее, используется оператором WORD в каче¬
стве временного буфера или области для запоминания, а ’’плавающая” зона, отстоящая от HERE на
188
фиксированное число байтов, является временным буфером, адрес которого сообщает PAD. Слово
PAD можно описать как
. PAD HERE n + ; ,
где n — фиксированное число (по крайней мере 65 в большинстве версий Форта и по меньшей мере
85 в Форт-83). Как и в случае HERE, легко подумать о PAD как о константе, а не как о слове, ко¬
торое вычисляет адрес. Как вы видели в предшествующих главах, PAD весьма полезен для времен¬
ного запоминания данных, так как он никогда не может быть в конфликте со словарем. PAD не
пригоден для длительного запоминания, потому что некоторые слова Форта используют его и из-за
того, что он смещается при изменении размера словаря.
В большинстве версий Форта стек параметров размещается в области старших адресов памяти и в
конце первых 64K байт (далее могут размещаться блочные буферы). Между PAD и стеком парамет¬
ров — переменное число свободных ячеек памяти (сам временный буфер), это число зависит от не¬
скольких факторов, включая размер памяти ЭВМ, от конфигурации Форта, размера словаря и чис¬
ла кодов в стеке параметров. Свободная память может лежать в интервале от нескольких до многих
тысяч байтов и может быть использована для системных процедур, таких как форматирование или
создание копий содержимого диска. Главным образом область свободной памяти предназначена для
использования словарем или стеком. Хотя словарь увеличивается от малых адресов к большим, стек
параметров в действительности растет от своего начала в сторону меньших адресов. Это позволяет
использовать свободную память как для словаря, так и для стека по необходимости. Вы можете по¬
нять, почему переполнение стека имеет катастрофические последствия. Переполненный стек может
наложиться на PAD, HERE и на верхнюю часть словаря, разрушив его. Это может, вероятно, слу¬
читься при беззаботном описании слов, которые оставляют в стеке что-то при зацикливании. И ко¬
нечно, размер словаря ограничен, так как нельзя двигать PAD дальше, чем на верх стека парамет¬
ров.
Стеки
Как контролируется содержимое стека? С помощью указателя. Если мы произвольно установим на¬
чало стека по адресу 1000, мы сможем представить шесть байтов области стека как
Адрес Содержимое
995 x
996 x
Указатель стека 997 2 LSB Верх стека (SP@ или 'S)
997 998 0 MSB
999 1 LSB
1000 0 MSB Начало стека
(S0 или S0 @)
где каждый адрес содержит старший (MSB) или младший (LSB) байт числа. В этом случае стек со¬
держит 1 и 2, а каждый символ ”х” проставлен для неопределенного байта. Число 2 находится на
верху стека, так как на адрес его младшего байта указывает указатель стека. Если мы положим в
стек 3 с помощью 3 <enter> таблица измениться:
Адрес
Содержимое
Указатель стека
995
3
LSB
Верх стека (SP@ или 'S)
995
996
0
MSB
997
2
LSB
998
0
MSB
999
1
LSB
1000
0
MSB
Начало стека
(S0 или S0 @)
Два байта, представляющие число 3, занесены в ячейку с адресами 996 и 995, а указатель стека
уменьшен на 2. Если вы теперь напечатаете DROP <enter> результатом будет
189
Адрес Содержимое
995 3
996 0
Указатель стека 997 2 LSB Верх стека (SP@ или 'S)
997 998 0 MSB
999 1 LSB
1000 0 MSB Начало стека
(S0 или S0 @)
Снова указатель стека просто увеличен на 2. Форт может стирать или не стирать 3, которая
’’удалена” из стека. Число 3 может остаться, так как любое новое число, положенное в стек, просто
заместит его там. Стек по существу — массив с полуавтоматической укладкой и извлечением чи¬
сел. Если вы немного знакомы с тем, как работает процессор, мы можем сказать, что обычно указа¬
тель стека параметров совпадает с указателем стека процессора. (Это обсуждается подробнее в
гл.16.) Большинство версий Форта имеет нестандартные слова, позволяющие вам найти адреса на¬
чала и верха стека. Наиболее часто адрес начала стека хранится в переменной пользователя, так
что SO @ заносит в стек этот адоес (MMSFORTH делает это), хотя в некоторых версиях SO само
засылает в стек этот адрес, т.е. @ не нужно. Адрес верхней ячейки стека засылается в стек опера¬
тором SP@ (засылка указателя стека — Stack Pointer Fetch), хотя некоторые модификации Форта
используют слово ’S (MMSFORTH использует SP@). Таким образом, если стек содержит
3 6 99
тогда
S0 @ 2 - @ S0 @ 4 - @ S0 @ 6 - @
отобразит на экране
3 6 99
в то время как
SP@ @ SP@ 2+ @ . SP@ 4 + @ .
выдаст на экран
99 6 3
С помощью этих слов можно описать другие полезные слова.
Например:
DEPTH ( n1 n2 — nl n2. .nn) SP@ S0 @ SWAP - 2 / ;
Мы попросим вас описать некоторые другие слова в качестве упражнений. Как вы знаете, с
целью экономии времени Форт не контролирует переполнение стека, так что только внимательное
программирование предотвращает разрушение словаря ниже стека. Извлечение кодов из пустого
стека намного более вероятно, так как довольно легко напечатать лишнюю точку с клавиатуры или
неправильно оценить число аргументов, необходимых слову Форта. В такой ситуации указатель
стека имеет значение больше, чем нижний адрес стека, т.е. если SP@ выдает число большее, чем
SO @. Фактически все варианты Форта проверяют, не случилось ли такое ёобытие при переходе к
пультовому режиму и при многих операциях вывода. Обычно, если зарегистрировано извлечение
кода из пустого стека, выдается сообщение об ошибке, а указатель стека устанавливается в началь¬
ное положение. Но возможно и разрушение системы без сообщения об ошибке. Например, если стек
пуст, следующая программа сделает это:
: B0MBIT 500 0 D0 DR0P L00P ;
Слово BOMBIT почти наверняка разрушит Форт, и потребуется перегрузка ЭВМ. Форт возлагает
больше ответственности за ошибки на программиста. Это одна из причин, почему Форт обладает
высоким быстродействием. Потребовалось бы очень много времени, чтобы контролировать перепол¬
нение стека каждый раз, когда изменяется его указатель. Хотя в MMSFORTH входной текстовый
буфер размещен за блочным буфером в области младших адресов, в некоторых вариантах Форта он
помещен выше стека параметров. Во всяком случае, стек возвратов, который обсуждается в гл.8,
обычно находится сразу за входным буфером или стеком параметров. Вы видели, что >R, R> и R@
позволяют использовать стек возвратов для запоминания значений из стека параметров и что стек
возвратов используется для запоминания индексов цикла, но это не его главная функция. Стек воз¬
вратов используется для записи адресов так, что Форт знает, куда вернуться, когда выполнение
слова завершилось. Это описано в деталях в разделе об исполнении слов Форта в гл.15. Область
между стеком возвратов и верхней границей памяти может использоваться для различных целей в
разных версиях. В MMSFORTH эта область может быть зарезервирована для дополнительных блоч¬
ных буферов, которые расширяют число буферов, размещенных ниже словаря, но это, конечно, co-
190
ответственно сокращает количество свободной памяти. Организация памяти, которую мы описывали
до сих пор, является типичной для ЭВМ, которые могут непосредственно адресоваться до 64K байт.
Но 16-битовые ЭВМ, так же как IBM PC, могут работать с lM байтом памяти и более, в то время
как числа одинарной длины не могут описать адрес более 65535. Существуют различные пути ре¬
шения проблемы, чтобы работать с большими массивами памяти в Форте. Может быть, простейшим
и наиболее общим является предоставление основной части Форта младших 64K байт (для про¬
грамм редко требуется больше памяти) и использование адресов двойной длины для адресации к
данным, лежащим выше. Например, MMSFORTH использует организацию памяти, которую мы
описали, но имеет слова для извлечения, запоминания, пересылки, резервирования и прочего ис¬
пользования памяти с адресами более 64K. Очень часто в этой области памяти организуется псевдо¬
диск. MMSFORTH использует сходную схему, но предусматривает возможность воспользоваться ме¬
такомпилятором для другого Форта в области старших сегментов памяти и работать с ним в даль¬
нейшем как с совершенно независимым языком (это делается, кбгда подготовлена новая версия
Форта). В тех случаях, когда программа со словарем больше, чем позволяет адресоваться 16-раз-
рядное слово, могут использоваться оверлеи, когда части словаря замещаются с диска по мере необ¬
ходимости (за счет потери быстродействия). По крайней мере, одна версия Форта, PC/FORTH+,
программно использует 32-разрядные, а не 16-битовые числа как для стека, так и для адресуемой
памяти (хотя и за счет места и скорости). Таким образом, память может адресоваться через обыч¬
ные коды и, хотя карта памяти организована так же, как было описано, программа может работать
со всей доступной памятью. Может быть, наиболее сложное использование памяти большей емкости
реализовано в HS/FORTH, где применены различные сегменты для различных частей описаний
слов, для различных контекстных словарей и для стеков, буферов и т.д. Таким образом, дополни¬
тельная память используется многими компонентами Форта, в то время как 16-разрядные числа
могут использоваться так же, как в Форте, в пространстве, ограниченном 64K байтами. Различные
схемы использования памяти в 16-разрядных ЭВМ является темой для другой книги. Лучший путь
понять, что где лежит, — это пропечатать на терминале большой кусок памяти. Мы предлагаем
вам сделать это в упражнениях следующего раздела этой главы. Даже если ваш Форт имеет опера¬
тор DUMP, вы можете найти следующее описание полезным, так как оно выдает как ASCII, так и
цифровое представление байтов.
: DUMP ( начальный адрес, число строк —)
CR BASE @ >R HEX ( спасение 9ASE, выбор шест
( надцатеричной системы)
16 * OVER + SWAR ( вычисление индексов цикла)
DO I 0 <tt tt tt tt tt tt > TYPE 2 SPACES ( печать номера строки)
16 0 DO ( Начало цикла по байтам)
I 4 MOD 0= IF SPACE THEN ( группируем байты по 4)
I J + C@ ( получаем байт)
0 <tt tt tt tt> TYPE SPACE ( печать байта)
LOOP ( цикл для 16 байтов)
CR 7 SPACES ( новая строка)
16 0 DO ( начало цикла для символов)
I 4 MOD 0= IF SPACE THEN ( группируем символы по 4)
I J + C@ DUP ( извлечение байта)
31 > 0VER 127 < AND ( печатный ASCII-символ?)
IF EMIT 2 SPACES ( если так, печатаем символ)
ELSE DROP 3 SPACES ( если нет, удаляем байт)
THEN ( конец ветвления)
LOOP CR ( цикл для 16 байтов)
16 +L00P ( шаг в 16 байтов на каждой строке)
R> BASE ! ; ( восстановление BASE по завершении)
Слово DUMP предполагает, что в стеке лежит начальный адрес и число 16-байтовых строк, кото¬
рые вы хотите отобразить. При выводе строки из 16 байтов пропечатывается шестнадцатеричный
начальный адрес, за которым следует сами байты. В следующей строке представляются ASCII-сим¬
волы для печатных байтов, отображенных в предшествующей строке. Выводимые символы во мно¬
гих случаях бессмысленны, но могут помочь вам найти откомпилированный текст или имена слов
Форта (если они записаны в незакодированном виде).
Вы можете захотеть пропечатать часть или весь ваш словарь с помощью печатающего устройства
(что потребует много страниц). Многие вещи, которые кажутся абстрактными и труднозапоминае-
191
мыми, становятся ясными, когда у вас есть четкая запись, в которую можно заглянуть. Это может
быть непрактично, если ваш Форт запоминает слова в нетипичном формате (например, различные
части слов записаны в разных сегментах памяти). Если вы действительно пропечатали вашу систе¬
му, будет полезным использовать цветные фломастеры для выделения каждого класса слов. Как
можно найти и идентифицировать слова, будет ясно из следующего раздела.
Упражнения
1. Переведите ЭВМ в шестнадцатеричную систему счисления и введите числа 0, 10, FF, F00 и FFFF в стек. Что вы ожи¬
даете получить, выдав команду SP@ 1 DUMP? Используйте DUMP, чтобы проверить ваш ответ.
2. Дайте ответ для упражнения 1 в случае чисел двойной длины 0., 10., FF., FFFF. и FFFFFFFF..
3. Как вы можете определить, какое самое длинное число может быть интерпретировано при вводе с пульта в вашем
Форте? Куда кладет Форт слово, которое компилируется?
4. Опишите С, и , (запятая), не используя ALLOT.
5. Если свободная память в вашем Форте лежит между PAD и верхом стека параметров, опишите слово .MEM, которое
выдает на дисплей величину емкости этой памяти.
6. Опишите PICK как NEWPICK, используя SP@ или ’S в зависимости от того, что приемлемо в вашей системе.
7. Опишите NEW.S используя SO. Не используйте ROLL.
8. Опишите ZERO-STACK, которое заполняет стек нулями, не меняя указателя стека. Не используйте DO-LOOP, приме¬
ните FILL.
Как слова Форта записаны в словаре?
Ключом к пониманию того, как работает Форт и почему он легко расширяем, является структура
слов Форта и то, как они записываются в словаре. (Помните, что существует много способов сде¬
лать это и что мы описываем наиболее общий из них с целью пояснения.) Термины Форта ”слово”
и ”словарь” действительно весьма близки к их исходному смыслу, так как (как и в русском языке)
слова Форта определены через другие слова, которые, сгруппированные вместе, образуют словарь.
Аналогия на этом не кончается: слова Форта могут быть организованы в контекстные словари и од¬
но и то же слово может даже быть использовано в нескольких разных контекстах.
Давайте рассмотрим один элемент словаря. Если мы введем:
: BASE? BASE @ DUP DECIMAL . BASE ! ;
почти все версии Форта скомпилируют его в Словарь в формате, содержащем поле имени и поле
связи (в совокупности называемые заголовком), а также поле программы и поле параметров (на¬
зываемые телом слова). Заголовок используется при поиске в словаре, в то время как тело управ¬
ляет тем, что слово должно делать. Четыре поля могут быть представлены в виде:
Заголовок Тело
(поле имени)(поле связи) (поле программы)(поле параметров)
Адресам первого байта имени, ячейки связи, программы и поля параметров слова часто присваи¬
ваются сокращения NFA, LFA, CFA и PFA. Так, если BASE? было скомпилировано, начиная с шес¬
тнадцатеричного адреса 7000, его представление в словаре может быть описано как
Заголовок Тело
Поле ИМЕНИ СВЯЗИ ПРОГРАММЫ ПАРАМЕТРОВ
Длина 4 байта 2 байт 2 байта (варьируется)
Адрес NFA=7000 LFA=7004 CFA=7006 PFA=7008
К несчастью, терминология Форта стала неоднозначной и путаной при использовании терминов
NFA, CFA, LFA и PFA. Они часто используются не только как адреса первого байта поля, но и как
содержимое поле. Так как все поля, кроме поля имени, содержат адреса (например, поле связи со¬
держит адрес или указатель, используемый при просмотре словаря), это сильно все путает. Мы ис¬
пользуем эти термины для обозначения только адресов полей и советуем вам поступать так же. Мы
будем ссылаться на содержимое поля связи, например, называя его именно так или, может быть,
как адрес или указатель, лежащий в поле связи. (Некоторые считают, что следует использовать со¬
кращения ANF, ACF, ALF и APF, чтобы избежать путаницы. Мы же чувствуем, что, введя больше
192
терминов, можно в действительности внести еще больший разнобой.) Давайте посмотрим, что из се¬
бя представляет каждое поле и что оно делает. Поле имени содержит имя слова, оно служит для то¬
го, чтобы было можно найти слово Форта в словаре, например, позволяя Форту исполнить слово,
имя которого было введено с последующим нажатием клавиши ’’возврат каретки”. Имя (такое как
’’BASE?”) кодируется каким-то способом (в зависимости от версии), прежде чем быть запомненным.
Мы скоро увидим подробнее, как кодируются имена. Следующая часть элемента словаря — поле
связи содержит просто указатель на другое слово, уже описанное в словаре. Этот адрес использует¬
ся, чтобы направить поиск на слово, описанное перед этим, обычно на его NFA. Слово, на которое
указано, может лежать непосредственно перед данным словом или заметно раньше, в зависимости
от структуры связи в конкретной версии. Полезность поля станет яснее, когда мы позднее в этой
главе обсудим контекстные словари. Поле программы содержит указатель на программу в машин¬
ных кодах, которая исполняется при использовании слова. Эта программа в машинных кодах опре¬
деляет, что делает слово и к какому типу оно относится. Все слова определенного типа (описания,
начинающиеся с двоеточия, константы, переменные и т.д.) имеют один и тот же адрес в своем поле
программы. Таким образом, константа (созданная оператором CONSTANT) имеет адрес в ее поле
программы, указывающий на программу в машинных кодах, которая копирует в стек число из его
поля параметров. Эта программа для каждого типа слов называется исполнительной. Адрес поля
программы всех описаний, начинающихся с двоеточия, указывает на исполнительную программу,
которая управляет выполнением слова в соответствии со списком адресов, хранящихся в поле пара¬
метров. Исполнение слов типа : и других слов рассмотрено подробнее в гл. 15. (Действительно ис¬
пользование поля программы, которое мы описали, представляет собой то, что называется косвенно
цепной программой. Существуют другие возможности, но они используются нечасто.) Длина поля
параметров слова варьируется значительно в соответствии с типом слова и способом его описания.
Она может в диапазоне от одного байта (как в словах, описанных CCONSTANT) до многих тысяч
байт (скажем, в массивах). Конкретное содержание поля параметров зависит от типа слова. Напри¬
мер, поле параметров константы, переменной и массива содержит последовательность байтов, или
данные, в то время как строка-константа будет иметь в поле параметров счетную строку. (Адрес,
выдаваемый любым словом, созданным с помощью CREATE, равен PFA этого слова.) Поле пара¬
метров слова типа двоеточие содержит адреса, обычно CFA слов, используемых в описании слова.
Если мы выделим CFA слов, используемых при описании BASE?, скобками ({...}), содержимое поля
параметров BASE можно будет представить как
Поле параметров
<BASE> Ш <DUP> {DECIMAL> {.} {BASE> <!> {EXIT}
В последней ячейке поля параметров любого описания типа двоеточие лежит адрес слова, названно¬
го EXIT, которое кладется туда оператором ; (точка с запятой). Слово EXIT необходимо, чтобы за¬
вершить исполнение слова и передать управление слову, из которого произошло обращение. (Это
исчерпывающе объяснено в гл.15.)
Как запоминаются имена?
Простейший способ запомнить имя слова BASE? в словаре заключается в том, чтобы записать
число букв в слове, после чего занести буквы в ASCII представлении, т.е. оформить как счетную
строку. Если записано имя полностью, тогда имеется возможность получить полный список слов в
словаре. Это делается посредством слов WORDS, VLIST или CATALOG, в зависимости от версии.
Данное преимущество компенсируется потерями в памяти и увеличением времени, необходимого
для поиска слова. Форт-79 и Форт-83 специфицируют только то, что различные слова должны
иметь до 31 символа и быть уникально закодированы и узнаваемы. Как это сделать, оставлено на
усмотрение разработчику языка. Общим нестандартным методом является запоминание числа сим¬
волов в исходном имени, за которым следуют только первые три символа имени.
В некоторых версиях пользователь может определить максимальное число запоминаемых симво¬
лов, изменяя величину системной переменной с именем WIDTH, прежде чем компилировать текст
программы. Опасность укорочения исходного имени до заданного числа символов заключается в
том, что длинные имена с равным числом символов могут оказаться не уникальными. Таким обра¬
зом, BASE? и BASE! следует переименовать на ?BASE и !BASE для того, чтобы они были различ¬
имыми. MMSFORTH использует необычную схему. Он кодирует (хэширует) слова так, чтобы вста¬
вить как можно больше информации в четыре байта поля имени. Таким образом, никакого допол-
7 АЛ К 193
/ М. Келли
нительного места в словаре не используется, так как имена укорочены, но вероятность неуникаль-
ности имени сведена к минимуму. Это в принципе соответствует регламентации стандартов, но не
буквально. Преимуществом является экономия места и сокращение времени поиска. К недостаткам
относится невозможность декодирования имен слов и, как следствие, недоступность списка слов в
словаре. Аналогичный метод кодирования имен вы можете применить и в вашем Форте. Так как
только 5 битов первого байта поля имени необходимо для кодирования максимального числа симво¬
лов (31), три остальных бита байта длины используются для других целей. B'MMSFORTH (и неко¬
торых других версиях Форта) первый бит поля имени равен 1 для слов немедленного исполнения
(например, если оно исполняется даже при компиляции слова типа двоеточие подобно .( ). В Фор¬
те, где запоминается все имя, старший бит первого и последнего символов (включая байт длины)
устанавливаются в единичное состояние, для того чтобы отметить начало и конец имени. (Так как
все символы — имени ASCII, их старшие биты равны 0.) Эта информация может быть использована
нестандартным словом TRAVERSE (см. стр.195). Другие свободные биты могут быть равны 0 или 1
в зависимости от того, откомпилировано ли слово без ошибок или допустимо ли, чтобы оно было
найдено в словаре при поиске. Бит, который используется для того, чтобы слово могло быть найде¬
но, называется бит-метка, для переключения этого бита используется нестандартное слово
SMUDGE. Имеет смысл упомянуть еще раз, что, хотя структура словаря, которую мы описали, яв¬
ляется наиболее распространенной, она не является единственно используемой. Могут применяться
другие варианты для языка в целом или только для отдельных слов. Например, можно компилиро¬
вать слрва с более чем одним полем программы, как это сделано в слове QUAN MMSFORTH, кото¬
рое использует три поля программы. Каждое CFA содержит указатель на исполняемую программу,
который придает словам QUAN три различных образа поведения, когда они используются с IS, АТ
и сами по себе. Слово F-83 Лаксена и Перри компилирует номер блока, откуда извлечена програм¬
ма, в заголовок слова, так что оператор VIEW может сообщить, в каком блоке лежит описание дан¬
ного слова. Возможны и более радикальные варианты. Функциональные элементы слов Форта (имя,
ячейка связи, поля программы и параметров) могут храниться даже в различных частях или сег¬
ментах памяти, как в HSFORTH и (немного иначе) в MacFORTH.
Нахождение частей слов
Вы уже знаете наиболее часто используемые слова Форта, предназначенные для поиска адресов
других слов в словаре ’ (апостроф), [’] и FIND, которые находят CFA или PFA в зависимости от
того, используете вы Форт-79 или Форт-83. И вы так же знаете о слове >BODY в Форт-83, которое
находит PFA для данного CFA, лежащего в стеке. Слово FIND в Форт-83 имеет совершенно другой
смысл по отношению к Форт-79. В стандарте 83 слово FIND предполагает наличие в стеке адреса
счетной строки. Затем FIND ищет эту строку в словаре и, если находит, заносит в стек CFA и 1
или -1 на верх стека. Если слово в описании типа двоеточие помечено как слово ’’немедленного ис¬
полнения”, в стек заносится 1, если же оно работает только в режиме исполнения — то -1. Если
строка в словаре не найдена, в стек заносится адрес исходной строки, а на верх стека — 0. В Форт-
83 слово FIND может использоваться не только подобно ’ с EXECUTE, но решение может быть
принято в зависимости от того, является ли слово оператором немедленного исполнения и найдено
ли слово вообще. В действительности FIND используется главным образом для внутренней работы
языка при компиляции, а не для целей программирования. Многие версии снабжены дополнитель¬
ными словами для вычисления адресов различных полей, выдавая PFA или NFA в стек. Их работа
пояснена ниже:
NFA "n-f-a" (PFA — NFA)
LFA "l-f-a" (PFA — LFA)
CFA "c-f-a" (PFA — CFA)
PFA "p-f-a" (NFA — PFA)
NFA использует нестандартное слово TRAVERSE, которое просматривает поле имени произволь¬
ной длины из конца в конец (в любом направлении), находя начальный или конечный байт, это
позволяет работать с именем любой длины. Хотя Форт-79 и Форт-83 категорически запрещают из¬
менение содержимого скомпилированных слов в стандартной программе, экспериментальный стан¬
дарт Форт-83 признает полезность нахождения адресов полей слова. Предлагаются следующие сло¬
ва:
>BODY "to-body” (CFA — PFA) (стандарт Форт-83)
>NAME "to-name" (CFA — NFA) (к полю имени)
>LINK ’’to-link” (CFA — LFA) (к полю связи)
194
BODY>
"from-body” (PFA —
CFA)
(от
PFA)
NAME>
"from-name" (NFA —
CFA)
(от
NFA)
LINK>
"from-link” (LFA —
CFA)
(от
LFA)
N>LINK
"name-to-link” (NFA —
LFA)
(от
имени
к LFA)
L>NAME
"link-to-name” (LFA —
NFA)
(от
LFA к
имени)
Если эти или аналогичные слова отсутствуют в вашем Форте, вы можете сами описать их. На¬
пример, используя структуру словаря, которую мы описали, с именами длиной в четыре байта, бу¬
дет довольно просто составить их описание:
>BODY ( cfa — pfa) 2 + ;
ИЛИ
L>NAME ( lfa — nfa) 4 - ;
Мы попросим вас попытаться описать такие слова в качестве упражнения.
Упражнения
1. Предположим, что версия Форта имеет поле имени длиной 4 байта, поле связи и поле программы по 2 байта. Опишите
остальные-предлагаемые Форт-83 слова, приведенные выше (>NAME, >LINK и т.д.).
2. Запишите положение слово HERE, введите описание BASE?, которое мы использовали, пропечатайте эту часть словаря
с помощью DUMP, для того чтобы посмотреть, можете ли вы идентифицировать его имя, поля связи, программы и парамет¬
ров, а также их содержимое. Используйте * (Форт-79) или * >BODY (Форт-83) с BASE? и выполните команду DUMP для
полученного адреса.
3. Опишите по крайней мере пять слов в форме : lDUMMY ;, : 2DUMMY ; и т.д. и пропечатайте посредством DUMP
каждый из этих элементов словаря, чтобы посмотреть, куда в предшествующем слове указывает содержимое поля связи.
Указывает ли каждое поле связи на предшествующее слово или какое-либо другое?
4. Найдите адрес исполняемой программы для слов типа двоеточие. Зная этот адрес, опишите слово COLONWORDS, ко¬
торое воспринимает два адреса со стека и находит CFA всех слов типа двоеточие, которые лежат между указанными адреса¬
ми.
Контекстные словари
Концепция контекстных словарей расширяет буквально смысл метафоры, примененной в
отношении определений слова и словаря. Контекстный словарь не используется в смысле ”ее разго¬
ворный словарь велик”, это не означает просто набор слов. Пожалуй, фразы ’’словарь джаза”, ’’ме¬
дицинский словарь” или ’’инженерный словарь” ближе по смыслу, вкладываемому Форт1. В Форте
контекстный словарь — это совокупность родственных слов, которые ищутся как целое, т.е. он
представляет собой секцию базового словаря. Другим подходом к этому могут быть контекстные
словари — это наборы слов, описания которых в словаре связаны друг с другом. Контекстные сло¬
вари Форта предоставляют вам контроль за тем, как производится поиск слов в словаре. Все про¬
граммы, которые мы составили до сих пор, использовали только один контекстный словарь — сло¬
варь FORTH, начинающийся словом с именем FORTH. Не удивительно, что первичный контекст¬
ный словарь имеет имя FORTH, часто поставляются два других контекстных словаря (называемые
EDITOR и ASSEMBLER). Первый содержит слова, используемые для редактирования текстов про¬
грамм, а второй включает слова для компиляции программ, написанных на ассемблере (см.гл.16).
Когда введено одно из слов EDITOR или ASSEMBLER, слова, принадлежащие к этому контекстно¬
му словарю, просматриваются первыми вне зависимости от того, когда они описаны или когда за¬
гружен словарь. Поиск в пределах любого контекстного словаря производится сверху вниз, т.е. на¬
чиная со слов, описанных только что, в направлении слов, загруженных раньше. Когда поиск в спе¬
циализированном контекстном словаре завершен, он продолжается в словаре FORTH (снова сверху
вниз). FORTH является словарем по умолчанию и всегда просматривается вне зависимости от того,
какой словарь или комбинация словарей просматривались первыми. С другой стороны, если испол¬
нено слово FORTH, тогда просматривается только контекстный словарь FORTH, а слова из других
словарей игнорируются.
Легко понять, что просмотр словаря не может происходить непосредственно от слова HERE до
слова FORTH, скорее, он следует структуре логического дерева, где контекстный словарь FORTH
1 В русском языке трудно подобрать отличные друг от друга слова для DICTIONARY и VOCABULARY. Последнее
переведено - контекстный словарь.- Прим. перев.
является стволом, а другие контекстные словари — ветвями, ведущими к стволу. Эта структура по¬
зволяет вам просмотреть или добавить любой контекстный словарь вне зависимости от того, где он
размещен в словаре. Предположим,что вы определили слово, использующее мнемонику ассемблера
со словом CODE (о программировании на Форт-ассемблере см.гл.16). Одним из действий CODE бу¬
дет то, что контекстный словарь ASSEMBLER просматривается первым, так что мнемоника ассемб¬
лера, которая компилирует машинные коды, будет находиться в словаре прежде, чем распознаются
любые другие слова с тем же именем. Это не только несколько ускоряет компиляцию программы на
ассемблере, но имеет важное следствие, позволяющее словам с идентичными именами иметь разные
функции в различных контекстных словарях. Какое ’’значение” имеет слово, когда определены не¬
сколько слов с одним и тем же именем, зависит от того, какое из них будет найдено первым; а это
задается порядком, в котором просматриваются контекстные словари. Имеется несколько способов
введения контекстных словарей, и фактически внутренние детали в различных реализациях могут
варьироваться значительно. Все контекстные словари в конце концов связаны со словарем FORTH,
но они могут быть связаны также друг с другом. Контекстные словари пользователя могут быть свя¬
заны непосредственно со стволом FORTH или образовывать цепочку веточка-ветка-ствол, согласно
которой и происходит поиск. Порядок просмотра для простой структуры будет выглядеть так:
(1) Контекстный словарь —> :
(2) Контекстный словарь —> :—> (4) Контекстный словарь FORTH
(3) Контекстный словарь —>
где числами (1) — (4) помечены альтернативы начальных точек просмотра словаря. Этот метод
связи словарей встречается в некоторых реализациях Форта, но более сложные методы позволяют
контекстным словарям образовывать цепи, как показано ниже:
(1) Контекстный —> :
словарь :—> (3) Контекстный —>.
(2) Контекстный —>: :—>(5) Контекстный
словарь словарь FORTH
(4) Контекстный словарь >
где снова просмотр может начинаться в одной из точек, помеченных цифрами (1) — (5), но если
первым просматривается контекстный словарь (1), то следующим перед FORTH будет словарь (3).
Эта иерархия возможных путей поиска является гибкой, но потенциально не однозначной. Хорошей
идеей является максимальное упрощение использования словарей, что обеспечит простоту чтения и
отладки вашей программы.
Контекстный словарь создается словом-описателем
VOCABULARY (name)
и, когда (name) исполнено, поиск будет начат именно с этого словаря. Адрес контекстного словаря,
который должен просматриваться первым, хранится в переменной пользователя CONTEXT, назван¬
ной так в соответствии с ее функцией (как, например, в предложении ’’класс в образовательном
контексте означает...но в контексте спортивной квалификации смысл его ...)1. Любое число слов с
идентичными именами могут быть доступны, если все они определены в разных контекстных слова¬
рях. Конечно, если одно и то же имя присутствует в одном и том же контексте словаре, использо¬
ваться сможет лишь то, которое найдено первым.
Если вы хотите добавить описание в словарь с именем (name), напишите
(name) DEFINITIONS
где слово DEFINITIONS имеет описание
: DEFINITIONS CONTEXT @ CURRENT ' ,
и где CURRENT является еще одной переменной пользователя, которая указывает на контекстный
словарь, который только что подключен к основному. Вы, вероятно, будете не так часто непосредст¬
венно пользоваться словами CONTEXT или CURRENT, в то время как VOCABULARY и
DEFINITIONS являются более удобочитаемыми и обеспечивают работу с контекстными словарями.
(CONTEXT и CURRENT включены в Форт-79, но удалены из базового набора слов Форт-83 пред¬
положительно из-за того, что они редко, если вообще когда-либо, используются программистами.)
' Следует еще раз подчеркнуть, что последовательность, в которой слова обнаруживаются при по¬
иске в контекстном словаре, определяется не их расположением в словаре, а лишь содержанием по¬
лей связи. Слова контекстного словаря ASSEMBLER, например, будут связаны с другими ассемб¬
лерными словами вплоть до самого последнего (т.е. слова ASSEMBLER). Это последнее слово будет
связано с последним описанным словом из контекстного словаря FORTH. Порядок просмотра слова¬
1 В оригинале не класс, а school в значениях школа и косяк рыбы.- Прим. перев.
196
ря задается при компиляции слова. Когда с помощью слова VOCABULARY создан новый контекст¬
ный словарь (пусть он имеет имя STATISTICS), в поле параметров слова STATISTICS зарезерви¬
ровано место для адреса слова, описанного в контекстном словаре STATISTICS последним. Каждый
раз, когда добавляется новое слово, адрес предшествующего заносится в поле связи слова, а адрес
нового слова укладывается в STATISTICS для использования при описании следующих слов. Когда
просмотр достигает конца контекстного словаря, начинается просмотр сверху вниз словаря (FORTH
или какого-то другого), в котором был описан предшествующий.
Мы описали два способа работы с контекстными словарями (связь только с FORTH или друг с
другом и с FORTH), и в обоих случаях мы предполагаем, что в словаре существует только один
ПУТЬ ПОИСКА. Пути поиска — это пути от конца словаря к его началу согласно указателям в
ячейках связи слов, входящих в контекстный словарь. Используя более чем один путь поиска, мож¬
но заметно сократить время компиляции исходного текста программы. Заметьте, то, что мы называ¬
ем путем поиска, другие называют ’’нитью”. Мы избегаем слова ”нить” в этом контексте, чтобы не
путать путь поиска с цепочкой программ в машинных кодах, которая работает при исполнении
скомпилированных слов и также называется ’’нитью” (’’thread”).
MMSFORTH предоставляет пример того, как можно использовать несколько путей поиска. Мы
видели, что MMSFORTH запоминает слова в словаре, используя закодированные имена, чтобы раз¬
личать слова со сходными именами. Если длина хэш-кода задана (0 — 31 в MMSFORTH), он мо¬
жет быть использован для управления поиском в словаре. В этом случае каждое имя контекстного
словаря (например, ASSEMBLER или FORTH) вместо того, чтобы иметь один указатель на слово,
описанное последним, имеет 32 таких указателя, по одному на каждый путь поиска. Когда слово
компилируется, его хэш-код определяет, с каким из путей просмотра контекстного словаря его свя¬
зать и где будет запомнен его адрес (в качестве будущего объекта связи для последующих слов того
же маршрута поиска).
Когда слово ищется, его имя снова кодируется (формируется тот же хэш-код) и определяется
маршрут поиска, где оно должно быть найдено. Таким образом, только 1/32 словаря должна быть
просмотрена, чтобы найти слово, что значительно ускоряет компиляцию, так как поиск — процесс
времяемкий. Если слово связано с маршрутом поиска на фазе компиляции, то, конечно, оно может
быть найдено позднее на том же самом пути поиска. Этот метод требует большего по размеру опи¬
сания слова и так же зависит от способа хэш-кодирования, который определяет номера маршрутов,
которые (в среднем) распределяются равномерно. Правильное использование большого числа марш¬
рутов просмотра всегда ускоряет поиск в словаре, независимо от того, используются ли контекстные
словари.
Вы помните, что слово FORGET служит для удаления слов из словаря. Ниже показано, как оно
работает. Формат обращения
FORGET (name)
если (name) отсутствует в текущем словаре, будет дано сообщение об ошибке. Слово FORGET ус¬
танавливает верхнюю границу словаря (указанную словом HERE и содержащуюся в переменной
пользователя DP) равной первому байту слова (name) (т.е. его NFA), эффективно удаляя все слова
и контекстные словари, описанные позднее. Может показаться, что FORGET не обращает внимания
на контекстные словари и просто укорачивает словарь до имени (name), но FORGET делает намно¬
го больше. Все маршруты просмотра во всех затронутых словарях должны быть поправлены
FORGET так, чтобы последующий поиск начинался с самого последнего оставшегося слова в кон¬
текстном словаре, из которого были удалены слова. Очевидно, что делается много больше, чем про¬
сто изменяется значение переменной DP. В некоторых версиях слово FORGET, прежде чем что-ли¬
бо делать, проверяет адрес, хранящийся в переменной пользователя FENCE, чтобы убедиться, что
FENCE содержит адрес меньше, чем слово, которое следует забыть. Переменная FENCE, которая
может быть задана как
HERE FENCE '
защищает все, что лежит в словаре ниже адреса, который в нее записан, от случайного стирания.
Хотя стандарты и не требуют наличия FENCE, эта переменная удобна для предотвращения потен¬
циально катастрофических ошибок. Хотя мы описали большинство наиболее употребляемых струк¬
тур словарей, могут использоваться и другие структуры. Применение контекстных словарей в Фор¬
те находится в процессе изменения (как и некоторые другие части языка). Существует несколько
хороших способов определить выборочный поиск в секции словаря, каждый со своими преимущест¬
вами и недостатками. Например, Форт-83 имеет экспериментальное предложение разрешить управ¬
ление порядком поиска путем декларации, а не во время компиляции. Манипулирование контекст¬
ными словарями в основном весьма просто; это делается тремя словами: VOCABULARY,
197
DEFINITIONS и FORGET, а так же именами индивидуальных контекстных словарей. В заключе¬
ние:
1. VOCABULARY (имя) создает контекстный словарь с именем (имя).
2. (имя) делает (имя) контекстным словарем (т.е. поиск начнется с него).
3. (имя) DEFINITIONS делает (имя) текущим словарем, в который добавляются новые описания.
4. FORGET (имя) удаляет (имя) и все слова, описанные после (имя), если (имя) принадлежит к
текущему словарю. Главные ограничения контекстных словарей связаны с тем, что они могут при¬
водить к путанице (в особенности когда связаны друг с другом) и к тому, что порядок поиска не
может быть изменен, если исходный текст откомпилирован. Большинство программистов старается
как можно меньше использовать контекстные словари, но, как мы увидим в следующем разделе,
даже с учетом перечисленных ограничений словари могут быть вполне полезными.
Упражнения
1. Ваш Форт, вероятно, имеет слово (называемое иногда VLIST, CATALOG или WORDS) для выдачи списка слов в кон¬
текстном словаре. Как вы можете просмотреть слова всех контекстных словарей в вашем Форте? Как вы думаете указанные
выше операторы находят слова, которые отображают?
2. Опишите новый контекстный словарь с именем A-VOC. Опишите четыре слова в словаре A-VOC, чтобы печатать неко¬
торые фразы, и присвойте имена с префиксом A-, последнее слово должно иметь имя A-LAST.
З.Отпечатайте список слов в A-VOC, которые вы описали. Сделайте FORTH контекстным словарем и повторите выдачу.
Что получилось?
4. Попытайтесь выполнить A-LAST при контекстном словаре FORTH. Что получилось? Сделайте контекстным A-VOC и
попытайтесь исполнить A-LAST снова.
5. Сделайте A-VOC контекстным словарем, опишите другой словарь B-VOC и добавьте несколько новых описаний, завер¬
шающихся словом B-LAST. Теперь посмотрите, можете ли вы исполнить A-LAST, находясь в B-VOC. Что говорит вам это о
структуре контекстных словарей в вашем Форте?
6. Опишите еще одно слово с именем A-LAST в контекстном словаре B-VOC (используя B-VOC DEFINITIONS, чтобы
быть уверенным, что вы пользуетесь нужным контекстным словарем). Как вы можете воспользоваться обеими версиями A-
LAST?
Использование контекстных словарей
Существует несколько причин использования различных контекстных словарей, а не только FORTH. Во-первых, вы мо¬
жете описать набор слов с идентичными именами, но с различными функциями, так что они не будут мешать друг другу.
Это весьма удобно при написании Форт-ассемблера, так как вы можете описать слова, управляющие программой, для ассем¬
блера, используя те же имена, что и в Форте, даже если их функции совершенно различны, т. e. при наличии отдельного
контекстного словаря ASSEMBLER слова IF, ELSE, THEN и т.д. могут без каких-либо неопределенностей сосуществовать в
общем словаре, имея те же имена, что и слова FORTH. Это позволяет сделать программирование на Форт-ассемблере во
многом идентичным программированию на Форте. При использовании в определенных контекстных словарях слова, имею¬
щие одинаковые имена, могут выполнять различную работу.
Другой причиной использования контекстного словаря является ускорение компиляции. Это становится особенно важным,
если ваша программа настолько велика, что вы вынуждены использовать оверлеи различных блоков для различных функций
в верхней области очень большого словаря. Каждый раз, когда загружается набор блоков, вы можете сэкономить на времени
компиляции, используя отдельный контекстный словарь для каждой функции. С точки зрения приоритета поиска здесь со¬
здается эффект помещения каждой функции ”в верхней части словаря”. Это существенно менее важно для Форта, который
при компиляции использует много маршрутов поиска. Заметим, что исполнение не ускорится, так как оно не включает в се¬
бя поисков в словаре (за исключением нахождения первого слова, чтобы начать исполнение программы).
Еще одним преимуществом нескольких контекстных словарей является то, что не все слова в конкретном словаре должны
быть описаны сразу. Вы можете использовать (имя) DEFINITIONS поочередно в различных местах программы, чтобы опи¬
сать порядок обхода родственных слов (объединив их в одном контекстном словаре). Это может быть удобно для последую¬
щего расширения набора команд в словаре.
Но чрезмерное увлечение контекстными словарями может привести к вредным привычкам просто потому, что добавление
слов в словарь когда попало не представляется разумным. Намного лучше держать контекстный словарь в одном месте (не¬
прерывный массив блоков), так чтобы с ним можно было работать как с единым логическим блоком. Вашу программу будет
легче читать и поддерживать.
198
Вот пример удобного использования контекстного словаря при создании процедуры "help”, которая переопределяет слова
Форта в словаре с именем HELPS. Новые операторы могут отобразить краткое описание функций слов словаря Форта. Это
может показаться теперь тривиальным, но представьте, что вы имели бы это, когда мы только начали изучать Форт.
Сначала создадим словарь HELPS:
VOCABULARY HELPS HELPS DEFINITIONS
Теперь введем несколько слов в словарь "HELPS":
: DUP ." = stack (n — n n) " CR ;
: DROP ." = stack (n -- ) ” CR ;
и т.д. Когда вы кончили, опишите слово HELP, которое используется следующим образом:
HELP (name) ,
что выдает описание, содержащееся в слове (паше). Для того чтобы слово HELP было найдено в любом случае, оно должно
быть описано, как слово слоэаря FORTH. Таким образом, его описание должно начинаться с
FORTH DEFINITIONS
HELP могло бы быть описано как
:HELP [COMPILE] HELPS FIND EXECUTE [COMPILE] FORTH ;
Слово HELP делает HELPS контекстным словарем для того, чтобы найти ваши полезные описания. Слово FIND находит
(имя) и исполняет его, а затем делает контекстным словарем FORTH. Слово [COMPILE] необходимо, если ваши контекст¬
ные словари являются словами немедленного исполнения (для дальнейшего обсуждения см. гл.15). Кроме того, пользователи
Форт-83 должны использовать * вместо FIND. Это описание опасно: в такой системе может произойти катастрофа, если
HELP будет использовано с (имя), которое не было описано в контекстном словаре "HELPS". Несколько лучше описание
HELP, приведенное ниже:
: HELP [dOMPILE] HELPS FIND ?DUP
IF [ FIND HELPS ] LITERAL OVER U<
( IF ['] HELPS OVER U<) (Форт-83)
IF EXECUTE ELSE DROP " not in HELP list.” THEN
ELSE .” not found.”
THEN [COMPILE] FORTH ;
Когда исполняется HELP (паше), HELPS делается контекстным словарем, так что новые описания будут найдены рань¬
ше, чем слова FORTH с тем же именем получат шанс быть исполненным. Слово FIND выдает CFA (имя) или 0, если поиск
не имел успеха. Если (name) найдено, контролируется его CFA, для того чтобы выяснить, было ли (паше) описано позже,
чем HELPS (функция [ FIND HELPS ] LITERAL будет прояснена в гл.15); если это так, слово из словаря HELPS исполня¬
ется, если же нет, имеется две возможности: или (name) не описано в словаре HELPS, или оно нигде не найдено, включая
словарь FORTH. При реализации этих вариантов выдаются соответствующие сообщения. В конце концов FORTH делается
контекстным словарем, так чтобы вы после выполнения HELP не остались в словаре HELPS. Этот пример должен показать
вам, как отдельные контекстные словари могут выполнять работу просто и понятно там, где другие методы труднопримени¬
мы.
Упражнения
1. Опишите HELP так, чтобы вы возвращались в контекстный словарь, из которого произошло обращение (а не всегда в
FORTH).
2. Опишите контекстный словарь "HELPS", который печатает описание слов из блоков на диске, а не из словаря, лежа¬
щего в памяти. Ограничьте каждое описанИе 32 или 64 символами (так чтобы 32 или 16 описаний помещалось в одном бло¬
ке). Каждое слово из HELPS должно использовать BLOCK для загрузки блока, где лежит нужный текст, вычислять адрес
начала текста и отображать его. Для формирования слов из набора HELPS, которые вам нужны, используйте слово-описа¬
тель HELPER (сосредоточьте ваши усилия вначале на первом блоке).
Выводы
В первой части главы мы обсудили организацию памяти и словаря Форта без практических примеров применения. Вы,
возможно, почувствовали, что материал, предложенный там, имел главным образом академический интерес. Но только бла-
/Годаря пониманию организации памяти и структуры слов Форта вы сможете вполне контролировать работу системы. В гл.15
мы обсудим, как работает интерпретация, компиляция и осуществляется выполнение программ. Вы убедитесь, что все, что
вы узнали об организации памяти и структуре словаря, весьма существенно.
Глава 15
ИНТЕРПРЕТАЦЙЯ, КОМПИЛЯЦИЯ И ИСПОЛНЕНИЕ
В гл. 14 мы рассматривали структуру Форта и организацию памяти. Теперь мы должны посмотреть, как он работает, т. e.
как интерпретирует, компилирует и исполняет слова и числа. Стандарты Форта благоразумно не делают попыток опреде¬
лить, как должен быть устроен язык изнутри. Задача стандартов — обеспечение функциональной совместимости программ, а
не подавление оригинальных путей адаптации Форта к новой технике. Но это означает, что мы можем только описать, как
работает типовая версия Форта. Несмотря на это, мы полагаем, что, предлагая типовой способ функционирования Форта,
мы дадим лучшее представление, как работает любой Форт.
Мы, пожалуй, напомним вам, что в Форте термины "интерпретация”, ”компиляция” и (в меньшей степени) ”исполне¬
ние” имеют иное значение, чем в теории вычислительной технике вообще. B Форте интерпретация состоит из приема слов
и чисел из входного потока через дисковые блоки или с клавиатуры, последующей компиляции введенного в словарь или не¬
посредственного исполнения. С другой стороны, в Бейсике и других интерпретивных языках интерпретация означает загруз¬
ку исходного текста в память, трансляцию его в машинные коды и выполнение строка за строкой каждый раз, когда про¬
грамма запускается. Другими словами, интерпретация в Форте происходит до компиляции и исполнения, в то время как в
других языках за интерпретацией всегда следует исполнение и не генерируется постоянная исполняемая программа. В Форте
компиляция состоит из формирования новых элементов словаря путем построения списка CFA слов, описанных ранее. С
другой стороны, компиляция в Фортране и других языках представляет собой трансляцию исходного текста программы в
программу в машинных кодах путем просмотра библиотеки процедур, хранящихся на диске, и укладку процедур в виде про¬
грамм в машинных кодах в исполнительный файл. Таким образом, компиляция в других языках представляет собой создание
программ в машинных кодах, в то время как в Форте это означает объединение существовавших ранее программ с помощыо
списка CFA в поле параметров слов. Эдмундс (1985) дает более полное описание обычного использования слов ’’интерпрета¬
тор” и ’’компилятор”. Исполнение во всех языках состоит из работы программ в машинных кодах, но в Форте, в противопо¬
ложность другим языкам, это делается с помощью нескольких коротких системных программ и адресов, скомпилированных в
описание каждого слова, чтобы сформировать цепочку заранее написанных машинных кодов, которые и выполняют работу
программы. Благодаря формированию этой цепочки или нити, на которую ’’нанизываются” машинные коды Форта, он за¬
служил название ”цепной ишперпретивный язык” (или TIL — Threaded-Interpretive Language).
Интерпретация
Привлекательность Форта происходит от простоты ввода последовательности чисел и слов с кла¬
виатуры, последовательности действий Форта после того, как вы нажали клавишу <enter>. Вы дела¬
ете это с самого начала каждый раз, когда сделали короткое вычисление или описание с клавиату¬
ры нового слова. Исходный текст на дисковых блоках воспринимается или интерпретируется Фор¬
том точно так же, как ввод с клавиатуры; фактически все вводы для Форта обрабатываются одним
и тем же способом. Все вводы в Форте приходят через входной поток, последовательность слов и
чисел, разделенных пробелами, вне зависимости от происхождения ввода.
Но как узнает Форт, где найти данные? Как Форт узнает слова и отличит их от чисел? Как узна¬
ет Форт, что делаТь с введенной информацией? В табл. 15.1 дана сводка того, что делает Форт по¬
сле инициализации. Подробное рассмотрение операций в табл. 15.1 ответит на некоторые наши
вопросы.
Функции, заключенные в скобки в табл. 15.1, могут выполняться, а могут и не выполняться в раз¬
личных версиях Форта.
Вы рассмотрели ABORT и QUIT в гл. 7, но в другом контексте. ABORT и QUIT возвращают уп¬
равление клавиатуре, так как они представляют собой, как показано в табл. 15.1, бесконечные цик¬
лы, присущие интерпретатору Форта. Задачей ABORT является приведение системы в исходное со¬
стояние и передача управления оператору QUIT, который исполняется до тех пор, пока не встре¬
тится ошибка. QUIT очищает стек возвратов, устанавливает режим исполнения и ожидает нового
ввода с клавиатуры. Как только что-то поступило на вход, производится интерпретация до тех пор,
пока не будет обнаружено ошибки (табл.15.2). В случае ошибки QUIT прерывает цикл с помощью
ABORT, чтобы вернуть систему в исходное состояние, прежде чем продолжить работу дальше.
(Когда вы применяли ABORT и QUIT в гл. 7, вы в действительности использовали их циклы, как
здесь и показано.)
200
Таблица 15.1. Обзор работы Форта
Инициализация системы
ABORT
BEGIN
Очистка стека параметров
(FORTH делается текущим контекстным словарем)
(Установка десятичной системы счисления)
QUIT
Установка режима исполнения
BEGIN
Очистка стека возвратов
Ожидание ввода с терминала
BEGIN
Если входной поток не иссяк и не обна
ружено ошибки
при интерпретации входной информации
(продолжение в таб.15.2), то
REPEAT
UNTIL ошибка
UNTIL всегда
Форт проводит большую часть времени внутри цикла QUIT. Здесь входной поток воспринимает-
ся, делится на отдельные слова и числа (называемые лексемами), которые в зависимости от обстоя¬
тельств, рассмотренных в табл. 15.2 компилируются, исполняются или заносятся в стек. Когда
входной поток иссяк, управление передается снова клавиатуре, готовой для нового ввода.
Как работает интерпретация в Форте, показано э табл. 15.2. Входной поток воспринимается с
терминала, если переменная пользователя BLK равна 0, или из блока, номер которого записан в
BLK. Входной поток подвергается разбору или разбивается на группы символов (или лексемы). Так
как разбор производится командой 32 WORD, лексемы должны отделяться друг от друга по крайней
мере одним пробелом (ASCII 32), что объясняет, почему пробел не может никогда быть частью сло¬
ва Форта. Лексема является, таким образом, просто группой ASCII символов, которая может быть,
а может и не быть действительным словом Форта или числом. Для каждой лексемы
Таблица 15.2. Интерпретация входного потока
Если входной поток не иссяк
Если BLK @ = 0, входной поток поступает с терминала
В противном случае входной поток поступает из блока,
номер которого лежит в BLK
Для каждой лексемы из входного потока..
Если лексема оказалась словом из словаря...
Если режим компиляции (STATE = 1)
Если слово немедленного исполнения, то оно
исполняется
В противном случае компилируется CFA слова в
словарь
В противном случае (STATE=0) слово исполняется
Если лексема не слово...
Если режим компиляции (STATE=1)...
Если число одинарной длины, компилируем с
помощью LITERAL
В противном случае (число двойной длины)
компилируем с помощью DLITERAL
В противном случае (STATE=0) заносит число в стек
В противном случае имеет место ошибка и выполняется
AB0RT
Повторять, пока не иссякнет входной поток или не воз
никнет ошибка
проверяется, не является ли она словом Форта, путем кодирования (если необходимо) и поиска в
словаре, как описано в гл. 14, чтобы определить, соответствует ли она имени какого-либо уже за¬
помненного слова.
Если лексема найдена в словаре, она является словом Форта и исполняется или компилируется в
зависимости от значения переменной пользователя STATE. Если STATE содержит не нулевое зна¬
чение, Форт находится в режиме компиляции, и, если только лексема не описана как слово немед¬
ленного исполнения, ее CFA компилируется в словарь оператором , (запятая) по адресу HERE. Ес¬
ли значение переменной STATE равно 0, Форт находится в режиме исполнения и слово выполняет¬
ся путем передачи его CFA оператору EXECUTE.
201
Если лексема не найдена в словаре, интерпретатор пытается преобразовать ее в двоичное число
для записи в память с помощью слова NUMBER. Если ка^сдый символ лексемы является цифрой с
величиной меньше текущего значения BASE, преобразование оказывается успешным. Например,
наибольшая цифра в десятичной системе (основание 10) равна 9, но наибольшая цифра в шестнад¬
цатеричной системе (основание 16) равна F шестнадцатеричному или 15 — десятичному; тогда
символ G приведет к ошибке преобразования, когда система шестнадцатеричная. Десятичная точка
указывает Форту, что лексема должна рассматриваться как число двойной длины (См. описание
слова NUMBER в гл. 9). Если лексема является действительно числом, величина STATE определит,
следует ли это число компилировать или исполнять, передав его в стек. Мы позднее расскажем вам
подробнее о том, как компилировать числа.
Если ошибки не случилось, Форт выдает на экран ”ОК”, чтобы сообщить, что все, что нужно,
сделано и что он готов для очередного ввода с клавиатуры. Но если лексема — не слово Форта и не
число или если при интерпретации или исполнении произошла ошибка, система, прежде чем про¬
должить процесс с самого начала, возвращается в исходное состояние onepaTopoMcfc ABORT.
Является ли источником входного потока блок или клавиатура, смещение относительно его нача¬
ла в любой момент во время интерпретации задается содержимым пользовательской переменной
>IN (”to-in”). Переменная >IN содержит число байтов от начала входного потока, где происходит
синтаксический разбор. Эта переменная всегда указывает на байт после пробела, следующего за по¬
следней лексемой. Переменная >IN постоянно корректируется во время интерпретации. Перемен¬
ные BLK и >IN описаны в гл. 10, но мы покажем так же некоторые их приложения в упражнениях.
Как уже обсуждалось в гл. 9, когда входной поток воспринят с клавиатуры (содержимое BLK
равно 0), символы уложены в зарезервированную область памяти, называемую текстовым вход¬
ным буфером, одновременно они отображаются на экране терминала. (Стандарты требуют, чтобы
текстовый входной буфер вмещал по меньшей мере 80 символов.) В Форт-83 (и в большинстве дру¬
гих версий) адрес начала этого буфера хранится в переменной TIB.
В гл. 9 вы видели, как использовать QUERY для ввода строк перед их разбором. Главная функ¬
ция QUERY — работа в интерпретаторе. В Форт-79 и большинстве других версий Форта ввод про¬
исходит через QUERY. Описание в Форт-79 будет выглядеть
: QUERY TIB 80 EXPECT 0 >IN! ;
Поскольку описание не устанавливает >IN на начало входного буфера или BLK равным 0, для
пользы дела мы должны описать что-то вроде
: GETINPUT 0 BLK ! 0 >IN ! QUERY ;
Теперь, если вы напечатаете
GETINPUT <enter>
курсор будет установлен после GETINPUT, ожидая вашего ввода. Теперь, если вы напечатаете,
скажем,
5 12 + . <enter>
на экране появится 17. Другими словами, GETINPUT использует QUERY, которое, в свою очередь,
использует EXPECT, чтобы передать в процессе исполнения введенную информацию во входной
буфер. Оператор QUERY не стандартизован в Форт-83, но если он включен (а так обычно и есть),
он должен быть определен иначе:
: QUERY 0 BLK ! 0 >IN ! TIB 80 EXPECT 0 >IN ! ;
В этом случае QUERY действует как GETINPUT.
Чтобы посмотреть, что содержится в текстовом буфере, нажатием соответствующей клавиши вве¬
дите семь пробелов и затем напечатайте
TIB 2 DUMP <enter>
Результат после DUMP будет выглядеть как
(адр) 20 20 20 20 20 20 20 54 49 42 20 40 20 32 20 44
T I В @ 2 D
55 4D 50 00 00 xx xx xx xx xx xx xx xx xx xx xx
U M P
ще ”(адр)” будет зависеть от вашей системы, а ”хх” — неизвестные байты. Вы можете убедиться,
что входной буфер является точной копией строки, которую вы напечатали, дополненной пробела-
202
ми в начале и, возможно, оставшимися символами от предшествующего ввода в конце (отмеченные
здесь ”хх”). Это связано с тем, что входной буфер заполняется оператором QUERY. Мы знаем, что
Форт интерпретировал входную строку, так как он исполнил DUMP, чтобы пропечатать две первые
строки текстового входного буфера.
Но как Форт знает, где остановить интерпретацию строки, введенной с клавиатуры? Это варьи¬
руется от версии к версии. Мы опишем, что происходит в MMSFORTH. Напечатайте ту же коман¬
ду, но на сей раз без предшествующих пробелов, и увидите
(адр) 54 49 42 20 40 20 32 20 44 55 4D 50 00 00 20 44
T I В @ 2 D U M P D
55 4D 50 00 00 xx xx xx xx xx xx xx xx xx xx xx
U M P
Оператор DUMP еще раз интерпретирован. Но почему MMSFORTH не пытается интерпретиро¬
вать ”DUMP”, который вы видите в конце, оставшийся от предшествующего ввода? Ввод с клавиа¬
туры выполняется оператором EXPECT в QUERY, который в данном случае мы завершили нажати¬
ем клавиши возврата каретки. Почему нет ASCII-кода возврата каретки (0D HEX) в конце введен¬
ного текста во входном буфере? Потому что вместо возврата каретки EXPECT в MMSFORTH вво¬
дит два ASCII-кода, равных 0 (нулевые байты), чтобы пометить конец ввода.
Более ранний ввод игнорируется, так как в MMSFORTH (и большинстве других версий) интерп¬
ретация текста завершается, когда во входном потоке встречаются символы 0, вставленные туда
оператором EXPECT. Это прерывание с помощью нулевых символов применяется не только для
ввода с клавиатуры, но и для блочных буферов; хотя дисковые буферы должны содержать 1024 бай¬
та, каждый из них занимает 1026 байтов памяти, так как нужны еще два ASCII-символа 0, чтобы
пометить конец каждого из буферов. В противном случае интерпретатор не будет знать, что конец
буфера уже достигнут, и может попытаться интерпретировать другие части памяти, что будет чре¬
вато неприятностями.
Другой способ контроля конца входного потока заключается в подсчете числа введенных симво¬
лов до нажатия возврата каретки. В Форт-83 это число для пультового буфера хранится в #TIB.
Еще один способ решения проблемы — это контроль того, что число, записанное в >IN, не превы¬
шает размера блочного или текстового входного буфера. В этом случае для каждого блочного буфе¬
ра требуется только 1024 байта, какие-либо разграничители для прерывания интерпретации не тре¬
буются.
Но как производится синтаксический анализ текста во входном буфере, чтобы слова могли ком^
пилировать или исполняться? Мы уже упомянули, что это делается словом WORD, и, если вы по¬
мните, как мы использовали WORD в PARSIT в гл. 9, вы сможете ответить на этот вопрос. Слово
WORD производит разбор буфера, выделяя последовательности символов, отделенные друг от друга
пробелами, и помещает их в виде счетных строк по адресу HERE. Слова затем используются для
поиска в словаре, и они либо исполняются, либо компилируются в соответствии с величиной
STATE, как мы уже описали.
Теперь мы можем видеть, как разграничитель 0 прерывает интерпретацию во многих версиях
Форта. ASCII-символ (0) воспринимается интерпретатором как имя слова (непечатное имя) Форта,
которое исполняется немедленно, чтобы остановить интерпретацию. Это слово иногда называется x,
а иногда ’’null”.
Все эти процессы, собранные в табл. 15.2, объединяются вместе и соответствует термину тексто¬
вый интерпретатор. Текстовый интерпретатор часто несколько произвольно называется внешним
интерпретатором, в противоположность внутреннему или адресному интерпретатору, который
является чем-то совсем другим, он обслуживается позднее в этой главе.
Упражнения
1. Во время интерпретации слово в зависимости от величины STATE либо компилируется, либо исполняется. Предполо¬
жим, что словарь был просмотрен и что CFA слова было извлечено. Опишите слово 7COMPILE, которое компилирует или
исполняет слово. (Подсказка: используйте в вашем описании , (запятую) и EXECUTE.) Это очень близко к тому, что дейст¬
вительно делается после успешного поиска в словаре.
2. Изучите слово FIND из Форт-83 (стр.155). Пусть FIND использовался и результат его работы записан в стек. Предпо¬
ложим также, что найдено искомое слово. Переопределите 7COMPILE как 83? COMPILE, чтобы при нахождении слова не¬
203
медленного исполнения оно выполнялось во время компиляции, в противном же случае исполнялось бы или компилирова¬
лось в зависимости от переменной STATE. Почему оператор FIND из Форт-83 неотъемлемая часть интерпретатора?
3. Опишите Форт-83 версию ’(апостроф) как N\ используя FIND из версии Форт-83. Если слово не найдено, должно вы¬
свечиваться "Word not found" (’’слово не найдено").
4. Модифицируйте 7COMPILE из упражнения 1 так, чтобы при компиляции их CFA отображалось на экране дисплея.
5. Попытайтесь напечатать >IN @ с клавиатуры. Теперь сделайте это, введя предварительно несколько пробелов. Что оз¬
начают полученные числа? Как используется >IN, когда входная информация поступает от клавиатуры?
6. Напечатав
0 >IN ! QUERY
вы вызовете "зависание" ЭВМ..Почему?
7. Предположим, что вы описали
: FAKE-LOOP 10 = IF QUIT THEN ;
далее напечатали
0 <enter>
а затем
1+ DUP DUP FAKE-L00P ) >IN ! <enter>
Что, вы полагаете, произойдет?
8. Пусть 0, или X, или null останавливает интерпретацию в вашем Форте. Опишите
: STOP 0 TIB >IN @ + ! ;
Теперь, если вы напечатаете
5. STOP 6
что вы увидите? Почему?
Компиляция
Как вы видели, когда текстовый интерпретатор встречает слово Форта, происходит одно из двух:
слово либо исполняется, либо компилируется в словарь Форта. Но как осуществляется компиляция
или исполнение? Рассмотрим сначала компиляцию.
Компиляция представляет собой процесс добавления в словарь новых слов, чисел или строк.
Обычно это делается в верхней части словаря (т.е. по адресу HERE). Мы видели в гл. 6, что 8- и
16-битовые величины могут компилироваться непосредственно с помощью С, и , (запятой). Слова,
такие как С, , которые расширяют словарь, не создавая новых описаний, называются компилирую¬
щими. Слова же, которые компилируют новые описания, такие как : (двоеточие) и CREATE, назы¬
ваются словами-описателями. В этом разделе мы рассмотрим, как осуществляют компиляцию наи¬
более часто используемые слова-описатели.
Теперь покажем, как работает : . Рассмотрим наш старый пример:
: BASE? BASE @ DUP DECIMAL . BASE ! ;
: (двоеточие) кодирует имя ’’BASE?” и запоминает результат (по адресу HERE) как поле имени в
элементе словаря BASE?. Если имя BASE? уже имеется в словаре, то будет выдано сообщение
’’duplicate name” (повторное описание). Слово : затем находит NFA предшествующего слова в сло¬
варе и запоминает в поле связи BASE?, чтобы обеспечить управление поиском. (Способ того, как
это делается, зависит от оговоренных условий связи контекстного словаря вашего Форта; см. гл.
14.) Этим завершается оформление заголовка BASE?. Следующим шагом будет компиляция адреса
программы в машинных кодах, используемая при выполнении слов типа двоеточие (называемая ис¬
полнительной программой типа двоеточие), в поле программы BASE?. Слово : может также выпол¬
нить самые разные процедуры, например разрешить слову ; детектировать ошибки компиляции.
После этого слово : устанавливает Форт в режим компиляции путем записи ненулевого кода в
STATE. На этом работа : завершится.
Следующий шаг весьма прост: каждое последующее слово из входного потока компилируется пу¬
тем нахождения в словаре и записи его CFA (с помощью ,) в новое описание. Это выполняется ав¬
томатически по мере разбора входного потока текстовым интерпретатором. Каждый раз, когда адрес
(или что-то еще) компилируется в словарь по адресу HERE, верх словаря смещается выше на соот¬
ветствующее число (переменная DP увеличивается), так что HERE всегда указывает на первый
204
свободный байт памяти, находящийся сразу после словаря. Результирующий список адресов в поле
параметров BASE? выглядит, как показано в гл. 14.
Слово ; (точка с запятой) завершает описание, начатое :, поместив CFA оператора EXIT в по¬
следнюю позицию тела оператора BASE?, возвратив Форт в режим исполнения и сделав значение
переменной STATE равным 0. Слово ; (точка с запятой) проверяет также, не было ли ошибки при
компиляции. Один из способов, каким ; может выявить ошибку, — это проверить, изменился ли
указатель стека в процессе компиляции. Другая схема предполагает, что : заносит в стек число, ко¬
торое должно там сохраняться к моменту выполнения слова ;, последнее его и удаляет. Если ошиб¬
ка произошла, ; возвращает переменной DP и, следовательно, HERE то значение, которое оно име¬
ло до :, предотвращая какие-либо изменения словаря. (Некоторые версии Форта оставляют в слова¬
ре заголовок и частично компилированное тело, просто устанавливая бит-метку в такое состояние
(см. гл. 14), что слово не может быть найдено. Это, однако, позволяет ошибкам использовать про¬
странство словаря.)
Откомпилированный в оператор BASE? представляет собой заголовок, состоящий из поля имени
и поля связи, и тело, составленное из указателя на исполнительную программу ’’двоеточие”, за ко¬
торым следует список CFA слов, используемых в описании BASE?, завершающийся CFA слова
EXIT. Вы можете убедиться, что это как раз то, что было скомпилировано в поле параметров
BASE?, с помощью пропечатки и записи каждого адреса, содержащегося там. Затем, если вы от¬
дельно проверите каждое слово, используемое в описании BASE?, с помощью
FIND (СЛОВО) U.
или в Форт-83
’ (слово) U.
вы убедитесь, что список адресов тот же, что и в поле параметров. Программа, которая выполняет
аналогичную функцию автоматически, отображая имена слов, использованных при описании конк¬
ретного слова, называется Форт-декомпилятором. Декомпилятор находит каждое слово, использо¬
ванное в описании по его CFA, которое, в свою очередь, используется для нахождения его имени.
Декомпилятор не может работать с MMSFORTH и другими версиями, которые используют хэш-ко¬
ды для имен.
Упражнения
1. Скомпилируйте следующие слова:
: 1DUMMY ;
: 2DUMMY 1DUMMY ;
: 3DUMMY 2DUMMY ;
пропечатайте каждое из них с помощью DUMP, определите адреса поля связи, поля программы и поля параметров каждого
слова и запишите их. Затем запишите содержимое каждого из полей.
2. Адреса, записанные в поле программы всех слов, одни и те же. Почему? В каком соотношении находится этот адрес с
адресом поля программы BASE? ?
3. Маршрут исполнения слова 3DUMMY проходит через шесть CFA (если вы включите в список три исполняемых EXIT).
Просмотрите поля параметров и выясните, что это за адреса.
4. Как бы вы могли изменить поле связи 2DUMMY, чтобы lDUMMY было нельзя обнаружить в словаре ? Это должно
быть сделано аккуратно и может оказаться невозможным, если в вашем словаре используется большое число путей поиска.
Проверьте ваши результаты.
5. Как вы можете изменить 3DUMMY, чтобы оно исполняло lDUMMY вместо 2DUMMY без повторной компиляции?
(Подсказка: измените что-то в PFA.) Как бы вы могли изменить его так, чтобы 3DUMMY не делало ничего (подумайте об
EXIT).
6. Найдите CFA операторов + и * и запишите их. Опишите следующее:
• PLUS/MINUS/TIMES * ;
Когда исполняется PLUS/MINUS/TIMES, перемножаются два числа. Измените это слово путем изменения его поля пара*
метров так, чтобы оно выполняло операцию вычитания одного числа из другого. А теперь сделайте так, чтобы оно произво¬
дило сложение двух чисел. Хотя в норме этого делать в программе не следует, это демонстрирует, что функция слова может
динамически меняться путем изменения содержимого поля параметров. (Это, конечно, может быть сделано более изящно,
если использовать исполнительный вектор, как в гл. 6.)
205
Исполнение при компиляции
Бывают случаи, когда хочется исполнить какое-то слово во время компиляции описания типа
двоеточие. Рассмотрим примеры:
: SAY-IT .’* I am compiling.” ; IMMEDIATE
: COMPILE-IT SAY-IT .” I am already compiled." ;
Когда вы компилируете COMPILE-IT, будет выдано на экран сообщение ”1 am compiling.”. Когда
же COMPILE-IT исполняется, вы увидите ”1 am already compiled.”. Но, когда вы исполните SAY-IT
с клавиатуры, будет выдано сообщение ”1 am compiling.”, что будет, конечно, неверным. Так как за
описанием SAY-IT следует слово IMMEDIATE, оно становится словом немедленного исполнения.
Вспомните из гл. 9, что ( и .( являются также примерами слов немедленного исполнения.
Слово IMMEDIATE сообщает Форту, что нужно пометить только что описанное слово, чтобы оно
исполнялось немедленно там, где встретится, вне зависимости от того, находится ли Форт в режиме
компиляции или исполнения. Действие слова IMMEDIATE заключается в том, чтобы устанавливать
первый бит поля имени только что описанного слова в единичное состояние. Этот бит воспринима¬
ется внешним интерпретатором как флаг, указывающий, что слово должно быть исполнено, где бы
не встретилось. Первый бит — это часто старший бит байта длины в словарях с кодированными
именами, в таком случае имя в словаре, чей первый байт больше шестнадцатеричного 7F, представ¬
ляет собой слово немедленного исполнения. Все слова, которые выполняют свою функцию при
компиляции, являются словами немедленного исполнения.
Необходимо соблюдать предосторожность при работе со словами немедленного исполнения. За¬
помните, что большинство версий Форта проверяет тем или иным способом, был ли изменен указа¬
тель стека во время компиляции (хотя это и не регламентируется ни одним стандартом). Таким об¬
разом, если слово немедленного исполнения изменит стек, это вызовет ошибку, когда ; обнаружит,
что стек в процессе компиляции изменился. Вы можете проверить это, описав
.IMM . ; IMMEDIATE
и затем
: TRY-IT .IMM ;
Вы почти наверняка получите сообщение об ошибке. Но, если вы опишите .IMM как
.IMM DUP . ; IMMEDIATE
и опишите TRY-IT, как и раньше, с 5 в стеке, цифра 5 может быть пропечатана или нет в зависи¬
мости от того, как проверяется стек во время компиляции. Это показывает, что вы должны описы¬
вать слова немедленного исполнения очень тщательно, так чтобы они не взаимодействовали с Форт-
компиляцией неожиданным образом.
Мы рассмотрим некоторые практические примеры слов немедленного исполнения в упражнениях,
но сначала давайте посмотрим, как слово немедленного исполнения может быть скомпилировано в
описание. Это делается с помощью слова [COMPILE]. Попробуйте этот вариант COMPILE-IT
LIAR [COMPILE] SAY-IT ." I am already compiled.” ;
При компиляции на экране ничего не появится, но, когда слово будет исполняться, вы увидите
I am compiling. I am already compiled.
Слово [COMPILE] заставляет скомпилировать SAY-IT, несмотря на то, что оно является словом
немедленного исполнения. Другими словами, [COMPILE] заставляет игнорировать старший бит в
SAY-IT и компилировать SAY-IT вместо того, чтобы исполнять во время компиляции.
Способом исполнения слова или слов, которые не относятся к числу слов немедленного исполне¬
ния, во время компиляции является использование слов [ (левая скобка) и ] (правая скобка). По¬
пробуйте написать
: SAY-IT-NOW [ .” I am compiling " ]
” I am already compiled " ;
В точности так же, как для COMPILE-IT, при компиляции SAY-IT-NOW на экране появится ”1 am
compiling.”, но при исполнении вы получите ”1 am already compiled.”. Слова между [ и ] исполня¬
ются, а не компилируются. Описания [ и ] просты
: [ 0 STATE ! ; IMMEDIATE
И
206
: ]. 1 STATE ! ;
Вы должны понимать, как они работают и почему ] не должно быть словом немедленного испол¬
нения, в та время как [ должно.
Слова [ и ] могут использоваться как в пределах описаний типа двоеточие, так и вне их. Вспомни¬
те, что при работе текстового интерпретатора в режиме компиляции, т. e. когда значение STATE
не равно 0, CFA любых слов из словаря, встречающихся во входном потоке, заносится по адресу
HERE, значение HERE соответствующим образом смещается. Попробуйте исполнить
HERE U.
] SWAP DROP [
HERE U.
и вы увидите, что значение слова HERE сместилось вверх на 4 байта, которые были добавлены к
словарю, что они содержат CFA слов SWAR и DROP, но не существует простого способа использо¬
вать их, так как они не могут быть найдены при просмотре словаря.
Но теперь предположим, что вы хотите скомпилировать исполнительный вектор с именем
CHOICES с вариантами ICHOICE, 2CHOICE и 3CHOICE. Вы уже знаете из гл. 6, что нужно сфор¬
мировать массив с помощью слова CREATE и, найдя CFA вариантов, записать их в массив с по¬
мощью слова , (запятая). Теперь посмотрим на это:
CREATE CHOICES ] 1CH0ICE 2CH0ICE 3CH0ICE [
Эта процедура создает идентичный массив с меньшими хлопотами и с много большей наглядностью.
Данная техника была использована для массива KEYVECTORS в редакторе в гл. 12.
Позже мы рассмотрим еще некоторые приложения скобок, но сначала несколько упражнений.
Упражнения
1. Для целей отладки иногда удобно знать, в каком блоке и где происходит компиляция. Опишите слово с именем .
LOCl, используя IMMEDIATE, которое, будучи введенным где-либо в блоке, отмечает позицию, выразив ее через номер
блока и относительный адрес в блоке. Модифицируйте это слово в LOC2 так , чтобы оно печатало номер блока и номер
строки в блоке (в предположении, что строка имеет 64 символа).
2. Предположим, что слово из упражнения 1 не было словом немедленного исполнения, но вы хотите его использовать
внутри описания типа двоеточие. Как вы можете это сделать?
3. Как можно отобразить содержимое стека, используя .S, когда компилируется описание типа двоеточие?
4. Что случится, если вы вставите [ без парной скобки 1 в описание типа двоеточие?
5. Могли л1| вы использовать [COMPILE], чтобы скомпилировать слово, которое не является словом немедленного испол¬
нения в описание типа двоеточие?
6. Что произойдет, если вы введете
[ 239 ]
в описание типа двоеточие? Результат будет зависеть от того, как контролируется состояние стека в вашей версии. Попро¬
буйте это с вашей версией. Если вы не получите сообщение об ошибке, проверьте состояние стека после этого.
7. Без отыскания CFA для +, -, * и / создайте массив с именем OPERATOR, содержащий CFA этих слов в указаннОм по¬
рядке. Теперь опишите слово с именем MATH, которое в соответствии с числом в стеке будет складывать, вычитать, умно¬
жать и делить два числа, занимающие вторую и третью позиции в стеке. Единице должно соответствовать сложение, двойке
- вычитание и т.д. Таким образом,
5 2 1 MATH
должно дать 7
5 2 3 MATH
должно дать 10 и т.д.
Компиляция чисел и текста
Существует слово LITERAL, задачей которого является взять число из стека и использовать его в
описании типа двоеточие так, что при его исполнении это число будет уложено в стек.
Попробуйте
: SILLY [ 2991 ] LITERAL ;
207
Если вы напечатаете
SILLY
вы увидите на экране число 2991, которое было занесено в стек словом SILLY. Но мы дали этому
слову такое имя (silly — глупый) потому, что оно было описано глупым образом. Вы могли бы точ¬
но так же описать
. SILLY 2991 ;
(Даже это глупо, так как следовало бы использовать константу; это бы заняло меньше места в сло¬
варе и обеспечило большее быстродействие.)
Для чего же пригодно слово LITERAL ? Давайте рассмотрим некоторые примеры. Вы помните,
что всегда полезно описать
: TASK ;
в начале программы, чтобы можно было ее удалить, написав
FORGET TASK
Давайте заставим. TASK делать что-то полезное. Если вы опишите
TASK ."Loaded from block” [ BLK @ ] LITERAL . ;
всякий раз, когда вы напечатаете TASK с клавиатуры, вам будет сообщен номер блока, где нача¬
лась программа. [ BLK @ ] кладет номер блока в стек, а LITERAL компилирует его на стадии
компиляции TASK. Всякий раз, когда используется TASK, отображается номер блока, где разме¬
щен исходный текст программы.
Может быть, наиболее важная функция LITERAL — ускорение исполнения программы. Предпо¬
ложим, что вы имеете константу
55 CONSTANT А
и вы должны многократно в процессе выполнения программы вычислять квадрат числа А. Вы може¬
те тогда описать
ASQRD А DUP * ,
но произведение будет вычисляться каждый раз, когда нужен квадрат. Если А действительно кон¬
станта, т. e. если она не изменяется в процессе исполнения программы, то
: ASQRD [ А DUP * ] LITERAL ,
будет выполняться быстрее, так как произведение будет определено на стадии компиляции, а не ис¬
полнения. Следует всегда стараться сделать все, что можно, во время компиляции, избегая потерь
времени при исполнении.
Слова-скобки часто используются, чтобы сделать программу более читаемой.
. RUGS ( n — sg-ft ) [ 9 12 * ] LITERAL * ;
вычисляет и компилирует 108 (квадратных футов), слово создано для умножения на число коври¬
ков, лежащее в стеке. Таким образом, 10 RUGS положит в стек 1080. Конечно, слово может быть
описано как
RUGS ( n — sg-ft ) 9 12 * * ;
или проще:
RUGS ( n — sg-ft ) 108 * .
но первый пример более удобочитаемый, чем другие, и работает так же быстро, как и последний; 9
12 * вычисляется только раз, когда RUGS компилируется. Сохранение длины и ширины в явном
виде делает более легким просмотр исходного текста программы и понятным размеры ковра, кото¬
рые имелись в ввиду. (Замечание: другим сцособом решения проблемы было бы описание
9 12 * CONSTANT 9BY12
использующие константу двойной длины для запоминания длины и ширины. Если вы затем опиши¬
те
• Ruds * ;
последовательность
12 9BY12 RUGS
будет однозначной, весьма удобочитаемой и столь же быстродействующей, хотя и требует большего
места в словаре из-за описания константы.)
208
Другой подход может позволить оператору выбрать размер ковра во время компиляции. Предпо¬
ложим что вы описали
WHATSIZE? .” Length” #IN ." Width" #IN * ;
и затем опишем заново
: CARPETS [ WHATSIZE? ] LITERAL * ;
Пользователь мог бы задать размер ковра, который нужно использовать при специальном прогоне
программы без изменения исходного текста. Обратите внимание, что если бы WHATSIZE было опи¬
сано как слово немедленного исполнения, то при использовании в CARPETS, его не следовало бы
помещать в скобки.
Мы предупреждали, что слова немедленного исполнения могут приводить к ошибкам, если они
изменяют состояние стека, когда используются в описании, начинающемся с двоеточия. Таким об¬
разом, хотя LITERAL компилирует число из стека, некорректно писать
555 : TAKENUM LITERAL ;
и ожидать, что TAKENUM оставит 555 в стеке. Почти наверняка будет выдано сообщение об ошиб¬
ке, так как LITERAL удаляет число из стека при компиляции. Это приведет к тому, что
TAKENUM будет помечено так, что его нельзя будет найти в словаре.
Теперь мы в состоянии понять, как в действительности компилируются числа в описании типа
двоеточие. LITERAL — слово немедленного исполнения, поэтому конечно, само не компилируется.
Вместо этого оно компилирует CFA другого слова (иногда называемого LIT), за которым следует
число из стека. Когда слово LIT исполняется, оно просто берет число, скомпилированное после не¬
го, и кладет его в стек. Числа, скомцилированные непосредственно (без LITERAL) в слово типа
двоеточие, также используют LIT, причем тем же самым способом.
LITERAL представляет собой пример слова с двумя совершенно разными функциями: первая ис¬
пользуется на фазе компиляции слова, вторая — при его исполнении. Так должно быть потому, что
компиляция числа из стека и укладка его в стек при исполнении — совершенно разные операции,
поэтому эти функции поделены между двумя словами LITERAL и LIT. Так как поведение
LITERAL при исполнении реализовано с помощью LIT, LIT называется исполнительной програм¬
мой LITERAL.
Как LITERAL компилирует CFA LIT в словарь? Ответ дает слово COMPILE. Хотя COMPILE вы¬
глядит подобно [ COMPILE ], его действие совершенно другое. В отличие от [ COMPILE ] при ис¬
пользовании COMPILE в описании типа двоеточие оно компилируется как любое другое слово, не
относящееся к ’’немедленному” типу. COMPILE что-то делает, только когда слово, в котором оно
использовано, исполняется. В этот момент оно берет CFA, следующее за его собственным CFA, и
компилирует в словарь по адресу HERE. Действие COMPILE называется отсроченной компиля¬
цией, так как оно не делает действительно ничего до тех пор, пока слово, в котором оно применено,
не будет исполнено. COMPILE выполняет компиляцию, даже если слово, в котором оно использова¬
но, является словом немедленного исполнения.
Таким образом, возможное описание LITERAL выглядит как
: LITERAL STATE @ IF COMPILE LIT . THEN ; IMMEDIATE
Когда LITERAL исполняется внутри описания типа двоеточие, отсроченное действие COMPILE за¬
ключается в компиляции CFA слова LIT по адресу HERE. Запятая затем компилирует число из
стека, которое будет нужно LIT, когда описываемое слово будет исполняться. Три фазы функцио¬
нирования компилирующих слов сходны с этапами работы слов-описателей, как видно из гл. 11. В
случае LITERAL этапами являются: 1 — компиляция LITERAL; 2 — исполнение LITERAL, когда
слово, в котором оно использовано, компилирует LIT, и 3 — исполнение LIT, когда слово, где оно
скомпилировано, исполняется.
Мы можем теперь понять, как работает слово .” . Подобно LITERAL, слово .” является словом
немедленного исполнения, которое имеет разные функции на фазе компиляции и исполнения. Ког¬
да .” встречается в описаний типа двоеточие, оно компилирует CFA своей исполнительной програм¬
мы (иногда называемой (.”)), а также следующий за ним текст, ограниченный .” и ” (двойная ка¬
вычка). Это делается командой 34 WORD, которая заносит счетную строку по адресу HERE как раз
туда, где ее следует скомпилировать (см. главу 9). Слово .” использует байт-счетчик счетной стро¬
ки, так же как ALLOT, чтобы выделить в словаре место, достаточное для данной строки и ее счет¬
ного байта. Когда слово, куда скомпилирована строка, исполняется, (.”) отображает строку, после
чего исполняются слова, следующие за строкой. Итак, описание .” может иметь вид
.” COMPILE (.”) 34 WORD C@ 1+ ALLOT ; IMMEDIATE
209
Это описание ." используется в Форт-83, где оно может работать только внутри описания типа дво¬
еточие. Но в Форт-79 .” может работать как внутри описания, так и вне его и оно описано несколь¬
ко иначе. Описание ." В Форт-79 могло бы выглядеть как
." STATE @ IF COMPILE (.”) 34 WORD C@ 1+ ALLOT
ELSE 34 WORD COUNT TYPE
THEN ; IMMEDIATE
Версия слова ." в Форт-79 является примером так называемых слов, зависящих от состояния, эти
слова делают разные вещи в зависимости от того, в каком состоянии находится система: в режиме
компиляции или исполнения. Вы можете вспомнить, что слово .( C’dop-paren” - точка-скобка)
стандарта Форт-83 используется для немедленного вывода текста, следующего за ним вплоть до
разграничителя) (правая скобка). Его описание имеет вид
.( 41 WORD COUNT TYPE; IMMEDIATE
Форт-83 не использует стандартных слов, зависящих от состояния, хотя ваши собственные слова и
могут быть такими.
Теперь должно быть ясно, что Форт имеет очень мощный компилятор, но в следующем разделе
мы покажем, как еще более мощные* компилирующие слова IF, ELSE, THEN, BEGIN и UNTIL мо¬
гут компилировать Форт-структуры, которые реализуют при использовании ветвления и зациклива¬
ния. Понимание их работы при компиляции позволит вам сконструировать свои собственные ком¬
пилирующие слова.
Упражнения
1. Опишите слова START-WHERE? так, чтобы, если описано
: TASK START-WHERE? ;
при исполнении TASK было бы сообщено, из какого блока было скомпилировано TASK.
2. Написана программа для перевода долларов в фунты стерлингов. Курс варьируется от дня ко дню, так что, когда про¬
грамма компилируется в начале каждого дня, необходим новый коэффициент пересчета. Опишите слово немедленного ис¬
полнения с именем ?RATE, которое при компиляции программы будет делать запрос текущего значения курса, а затем вы¬
даст его значение в стек. (Разместите слово в свободном блоке для загрузки.)
3. Используя слово из упражнения 2, опишите слово с именем CONVERTS, которое при компиляции запрашивает теку¬
щее значение курса, а когда исполняется, преобразует доллары в фунты согласно объявленному курсу. Подберите соответст¬
вующие масштабы коэффициента для входных и выходных величин (запишите слово в тот же блок, что и в упражнении 2).
4. Теперь опишите три слова с именами ENGLAND, DENMARK и GERMANY, которые при исполнении будут перево¬
дить доллары в фунты , кроны и марки. Слова должны использовать 7RATE, и они должны воспринимать ежедневное зна¬
чение курса для каждого вида валюты при компиляции, делая запрос "Dollars to kroners, current rate?". (Снова используйте
тот же блок.)
5. Опишите слово ?СОМР, которое будет печатать "Compile Only!” и выполнять ABORT, если слово, в котором оно при¬
менено, используется в режиме исполнения (?СОМР часто используется в описаниях компилирующих слов, таких как
LITERAL, чтобы предотвратить их неправильное применение вне описаний типа двоеточие).
6. Что отобразит на дисплее следующая текстовая последовательность?
: TEST COMPILE DUP ; IMMEDIATE
: -TEST1 5 TEST ;
TEST1 . .
Почему TEST должно быть словом немедленного исполнения? Что бы произошло, если вы напечатали TEST <enter> ? (Под¬
сказка: COMPILE содержит слово ?СОМР.)
Компиляция условных операторов Форт
Теперь нам следует посмотреть, как организуют работу IF...ELSE...THEN. Существуют две при¬
чины, почему мы этого хотим: познакомить вас подробнее с условными переходами и привести еще
несколько примеров применения COMPILE. Мы рассмотрим сначала IF....THEN. Вот один из спосо¬
бов, каким их можно описать:
: IF 7C0MP COMPILE 7BRANCH HERE 0 , , IMMEDIATE
: THEN 7C0MP HERE OVER - SWAP ' ; IMMEDIATE
210
?СОМР предотвращает их использование вне описаний типа двоеточие. Как вы видели в предшест¬
вующих упражнениях, его описание могло бы иметь вид
: ?СОМР STATE @ 0= ABORT" Compile only!” ;
Когда во входном потоке при компиляции встретится оператор IF, он первым делом с помощью
COMPILE 7BRANCH скомпилирует CFA слова ?BRANCH (7BRANCH иногда называют
OBRANCH) в верхнюю ячейку словаря, затем с помощью HERE занесет адрес следующей свобод¬
ной ячейки словаря в стек и, наконец, запишет 0 по этому адресу (0 будет заменен другим числом,
сформированным оператором THEN). Слова между IF и THEN компилируются обычным порядком.
К моменту исполнения THEN, адрес кода 0, скомпилированного оператором IF, все еще лежит в
стеке. Слово THEN вычисляет смещение адреса, оставленного оператором IF, по отношению к вер¬
ху словаря. Это осуществляется командой HERE OVER — . Данное смещение затем запоминанием
в ячейке, зарезервированной IF (посредством 0), с помощью команды SWAP !. Конечно, ни слово
IF, ни слово THEN сами не компилируются, так как являются словами немедленного исполнения.
Мы можем понять работу IF... THEN яснее, рассматривая поле параметров слова. Как вы знаете
из гл. 8, IF выполняет передачу управления слову, следующему после THEN, если в стеке лежит 0,
в противном случае исполняются слова, расположенные между IF и THEN. Если мы опишем слово
: N0N-ZER0? IF .” Non-"
THEN .” Zero" ;
мы можем убедиться, что если при исполнении NON-ZERO? в стеке окажется ненулевое число,
дисплей отобразит ”Non-Zero”, в противном случае будет отпечатано’^его”. Теперь, когда мы зна¬
ем, как работает IF...THEN, мы можем изобразить карту поля параметров слова NON-ZERO?
Поле параметров
Содержимое 9BRANCH смещение ( ”) (4Non-) (.”) (4Zero) (EXIT)
Байты 2 2 2 5 2 5 2
где (4Non-) и (4Zero) представляют собой счетные строки, скомпилированные оператором .”. Сме¬
щение будет равно 7 и указывает на CFA второго (.”).
Теперь о том, как работает то, что скомпилировано IF...THEN. 7BRANCH является исполнитель¬
ной программой для IF, и, если в стеке 0, управление передается вперед на число байтов, заданное
величиной смещения. Это делается путем добавления этого смещения к собственному адресу в PFA
NON-ZERO?, Форту предписывается продолжить исполнение, начиная с указанного адреса. В
случае NON-ZERO? исполнение возобновится со слова .” , предшествующего ”Zero”. Если
7BRANCH обнаруживает в стеке ненулевое число, передача управления осуществляется со смеще¬
нием в два байта, в результате выполняется ” Non-” и .” Zero”.
Заметьте, что некоторые версии Форта используют абсолютный адрес, а не смещение к адресу
для передачи управления оператором 7BRANCH. В этом случае описание IF будет тем же, а THEN
следует описать как
THEN 7C0MP HERE SWAP ! ; IMMEDIATE
Таким образом, после 7BRANCH будет записан адрес передачи управления, а не смещение.
7BRANCH, конечно, использует тогда запомненный адрес передачи управления, а не вычисляет
его, используя смещение. Применение адреса вместо смещения обеспечивает большее быстродейст¬
вие, так как не требует вычислений, прежде чем выполнить переход. Вы можете описать и распеча¬
тать описание NON-ZERO?, чтобы понять, какой метод реализован в вашем Форте. MMSFORTH
использует абсолютные адреса переходов, а не смещения.
ELSE работает в какой-то мере аналогично комбинации IF и THEN. Его описание может выгля¬
деть как
: ELSE 7C0MP COMPILE BRANCH HERE 0 ,
SWAP HERE 0VER - SWAP ! ; IMMEDIATE
Когда ELSE встречается в тексте, последовательность COMPILE BRANCH HERE 0 делает то же,
что и IF, но вместо CFA 7BRANCH . компилируется CFA BRANCH. Последнее слово является ис¬
полнительной программой ELSE. После BRANCH компилируется 0, адрес которого остается в стеке
для использования оператором THEN. Но вспомним, что в стеке лежит адрес, оставленный IF, поэ¬
тому необходима команда SWAP, прежде чем выполнять HERE OVER — SWAP ! для вычисления и
запоминания смещения в ячейку, зарезервированную оператором IF. Это смещение указывает на
211
CFA, которое следует сразу за 0, оставленным оператором ELSE. Адрес 0, который скомпилирован
ELSE, теперь лежит в стеке, и THEN заменит его, так же как THEN заменяет 0, следующий за
7BRANCH в предшествующем примере.
Что делает ELSE, может быть прояснено на следующем примере. Если мы опишем слово
: FLAG? ( n — ) IF .” True "
ELSE ." False ”
THEN ." Flag” ;
поле параметров FLAG? будет содержать
7BRANCH (смещение 1) (.") (5True)
BRANCH (смещение 2) (.”) (6False)
(.”) (4Flag) (EXIT)
При исполнении, если в стеке 0, 7BRANCH осуществляет переход к позиции непосредственно за
(смещением 2), т.е. к оператору (.”) для ’’False”, скомпилированному .” , следующим за ELSE в
исходном тексте. В противном случае ?BRANCH ’’перепрыгивает” через (смещение 1) и исполняет
(.”), предшествующий ’’True”, а затем переходит к BRANCH. BRANCH вычисляет адрес продол¬
жения выполнения программы, используя (смещение 2), и переходит к фразе .’’Flag”, следующей
за THEN в исходном тексте программы. BRANCH делает то же самое, что и ?BRANCH, но осуще¬
ствляет переход вне зависимости от состояния стека.
Необходимость помНить об отличиях компилирующих слов во время компиляции и при исполне¬
нии может показаться утомительной. Следует лишь помнить, что любое компилирующее слово вы¬
полняет две функции: одну — на фазе компиляции, когда производится компиляция исполнитель¬
ной программы данного слова и следующих за ней числа, смещения, адреса или текста, и вторую —
на фазе исполнения, т.е. когда скомпилированное слово исполняется. Теперь вы знаете, как разо¬
браться в работе большинства слов путем анализа их поля параметров. Давайте попытаемся это
сделать на ряде примеров.
Упражнения
!. Опишем
. L00K-AT-IF ( flag --) IF 1 ELSE 2 THEN 3 ;
С помощью пропечатки поля параметров как бы вы нашли положение LIT (или его эквивалента) и положение 1, 2 и 3 (за¬
помните, что некоторые малые числа заносятся в стек словами Форта)? Используя эти позиции в качестве контрольных то¬
чек, как бы вы нашли откомпилированное CFA операторов 7BRANCH и BRANCH (или их эквивалентов) и адреса или сме¬
щения, следующие за ними? Как можно определить, используют ли команды ветвления смещение или адрес?
2. Опишите .CMP для отображения величины HERE и содержимого стека во время компиляции (используя .S).
3. Введите .CMP перед IF, ELSE и THEN и после 3 в LOOK-AT-IF. Объясните полученные значения в стеке и измене¬
ние величины HERE. Не забудьте, что : (двоеточие) может положить что-то в стек.
4. Для иллюстрации предположим, что CFA LIT равна 174. Если вы опишете
: TEST [ 174 , 5 , ] . ;
Что будет сделано?
5. Предположим, что LIT или его эквивалент не существуют в качестве поименованного слова. Используя только его CFA,
опишите NEWLITERAL. Опишите NEWLITERAL так, чтобы оно работало как LITERAL, но с числами двойной длины в
стеке.
6. Предположим, что 7BRANCH и BRANCH не описаны в вашей системе. Используя CFA эквивалентов, дайте описание
для NEWIF и NEWELSE. Предполагается, что осуществляется абсолютное, а не относительное ветвление.
Слова без заголовков
Слова без заголовков в соответствии с определением лишены имени и поля связи, а имеют толь¬
ко тело. Конечно, они не могут быть найдены в словаре, но, поскольку они имеют тело, т.е. поле
программы и поле параметров, они могут быть исполнены. В действительности, когда вы в предше¬
ствующем наборе упражнений описали LITERAL, не имея слова LIT, вы использовали LIT как
слово без заголовка. Векторное исполнение (смотрите гл. 6) так же игнорирует заголовки слов (так
как EXECUTE требует для исполнения только CFA слов). Слова без заголовков могут использо-
2l2
ваться для того, чтобы их нельзя было найти в словаре. Иначе они являются ’’спрятанными” сло¬
вами (хотя, как вы знаете, по слову LIT умный программист может, вероятно, их найти).
Стратегия создания слов без заголовков заключается в создании в словаре структур, которые
имеют все необходимое слову для исполнения, кроме имени и поля связи. Конечно, слова без заго¬
ловков нуждаются в поле программы, содержимое которого должно указывать на исполнительную
программу, соответствующую типу слова, которое вы создаете. Мы приведем здесь пример, как со¬
здать слова типа двоеточие без заголовка, хотя константы, переменные и другие слова без заголов¬
ков могут создаваться согласно тем же правилам.
Давайте создадим слово без заголовка, эквивалентное нашему старому другу BASE?. Сначала
нам нужно знать адрес исполнительной программы описаний типа двоеточие. Для этого достаточно
воспользоваться оператором FIND (в Форт-79) или ’ (в Форт-83) для любого слова типа двоеточие,
с тем чтобы получить CFA и занести его содержимое в стек. Если вы оставите этот адрес в стеке,
приведенная ниже программа сформирует описание BASE? без заголовка, оставив его CFA в стеке
для последующего использования описанного слова:
HERE SWAP , ] BASE @ DUP DECIMAL . BASE ! EXIT [
Слово HERE заносит в стек верхний адрес словаря, который станет адресом поля программы нового
слова без заголовка.
Последовательность SWAP, компилирует адрес исполнительной программы слов типа двоеточие,
который вы оставили в стеке. Слово ] осуществляет переход в режим комиляции и слова, начиная с
BASE по ! будут скомпилированы в поле параметров слова без заголовка точно так же, как это бы¬
ло в обычном описании. CFA слова EXIT компилируется аналогично тому, как это делает ; в стан¬
дартном описании. Затем [ возвращает систему в режим исполнения. CFA слова без заголовка (за¬
писанное по адресу HERE) — все еще в стеке; для того чтобы предохранить его от потери, мы мо¬
жем спасти его с помощью описания
CONSTANT BASE?
теперь введение
BASE9 EXECUTE
исполнит "беззаголовочная” версию слова BASE?.
Конечно, намного проще описать BASE?, как обычно, — через двоеточие. Зачем нам все эти до¬
полнительные хлопоты? Версия BASE? без заголовка не может быть найдена в словаре и не может
быть исполнена или скомпилирована в другие слова без дополнительных усилий. Но эти недостатки
слов без заголовка являются и их достоинствами, так как они недоступны и не могут чему-либо по¬
мешать. Это важно для некоторых программ, которые при неправильном использовании могут раз¬
рушить систему. Многие внутренние программы Форта откомпилированы в виде слов без заголов¬
ков, потому что они практически бесполезны для чего бы то ни было, кроме внутренних задач Фор¬
та. (MMSFORTH использует массив MMS, где содержится набор системных слов без заголовка. На¬
пример, MMSFORTH-эквивалент 7BRANCH является третьим элементом MMS.) Слова без заго¬
ловков полезны также, чтобы предотвратить несанкционированные манипуляции с откомпилиро¬
ванной программой, — действительно, целые прикладные Форт-программы компилируются с ис¬
пользованием слов без заголовков, хотя и без использования этого метода. (В вашем Форте, воз¬
можно, предусмотрена компиляция беззаголовочных программ. MMSFORTH использует для этой
цели системную программу TEMP-HEAD.)
Это дает представление о том, насколько гибкой может быть Форт-компиляция. Хотя в отличие
от других языков в Форте компиляция и не генерирует программу в машинных кодах, она и не
должна это делать; каждое слово Форта в конце концов указывает на программу в машинных кодах
и исполняет ее. Форт — один из немногих языков программирования, который позволяет модифи¬
цировать собственный компилятор, используя программы высокого уровня.
Упражнения
1. Создайте два слова без заголовков, одно, чтобы пропечатать ”Word#0”, и второе, чтобы пропечатать "Word#l". Испол¬
ните оба слова, используя константы с именами OWORD и lWORD. (Запоминание адреса исполнительной программы для
операторов типа двоеточие в константе COLON-ADDR может упростить решение задачи.)
2. Как бы вы откомпилировали слова без заголовков OWORD и lWORD в описание типа двоеточие?
(Подсказка: используйте скобки и , (запятая).)
213
3. Используйте CREATE...DOESE> для создания слова-описателя, производные слова которого будут исполнять OWORD,
если в стеке лежит 0, и lWORD, если в стеке лежит 1. (Этот метод может быть использован для создания очень сложных
для понимания исходных текстов.)
4. Напишите слово-описатель GIVE-NAME, которое, формируя производные слова, требует наличия в стеке CFA описан¬
ного перед этим слова без заголовка. Когда производные слова исполняются, они должны исполнять соответствующее слово
без заголовка.
5. Опишите слово без заголовка (может быть, 2WORD) и оставьте его CFA в стеке. Затем опишите слово типа двоеточие и
скомпилируйте CFA слова без заголовка так, что оно будет использовано словом типа двоеточие. (Будьте осмотрительны при
компиляции слова типа двоеточие, следите за состоянием стека.)
6. Если в вашем Форте ?BRANCH и BRANCH не имеют имени, как бы вы описали IF и ELSE согласно приведенному
нами ранее алгоритму?
7. Сделайте то же, что и в упражнении 6, но опишите LITERAL, используя безымянное LIT.
Исполнение
Мы видели, как происходит компиляция; ну а теперь, что вы скажете об исполнении? To есть,
что случится после того, как мы откомпилировали слово и затем напечатали его имя или использо¬
вали EXECUTE при наличии его CFA в стеке? Давайте сначала посмотрим весь процесс в целом,
затем перейдем к рассмотрению деталей механизма.
Мы можем увидеть весь процесс исполнения в Форте, описав следующие слова:
1LEVEL .” The lowest level ” CR ,
. 2LEVEL ” Begin 2LEVEL " CR 1 LEVEL " End 2LEVEL " CR ,
3LEVEL .’’ Begin 3LEVEL " CR 2LEVEL " End 2LEVEL " CR ,
Когда исполняется 3LEVEL, на экране отображается
Begin 3LEVEL
Begin 2LEVEL
The lowest level
End 2LEVEL
End 3LEVEL
ok
Это показывает, что 3LEVEL использует слово 2LEVEL, которое, в свою очередь, использует
lLEVEL, в соответствии с тем, что мы потребовали в описании. Когда исполнение lLEVEL завер¬
шено, управление передается назад к 2LEVEL, а затем к 3LEVEL, и в конце концов появится от¬
клик ”ок”, сообщающий о готовности к новому вводу. Заметьте, что, когда начинает исполняться
3LEVEL, используя 2LEVEL (и когда стартует 2LEVEL, используя lLEVEL), Форт должен запом¬
нить, куда следует вернуться по завершении задания низкого уровня. Итак, как же Форт отслежи¬
вает порядок исполнения?
Давайте проследим исполнение 3LEVEL, просмотрев описание. Необходимо несколько указате¬
лей. Один мы назовем указателем инструкции или IP, он отслеживает слово, которое должно быть
исполнено следующим , и первоначально указывает на последовательность .” Begin 3LEVEL ”. Этот
указатель переходит от слова к слову, пока не встретит 2LEVEL внутри описания слова 3LEVEL.
Теперь IP должен двинуться внутрь 2LEVEL, указывая на .” Begin 2LEVEL ”, но сначала нужен
другой указатель, чтобы хранить информацию о том, куда вернуться в описании 3LEVEL. Второй
указатель должен указывать на последовательность .” End 3LEVEL”. После того как 2LEVEL от¬
образит ” Begin 2LEVEL ”, та же проблема возникает вновь. Третий указатель нужен, чтобы отсле¬
живать место, куда возвращаться в описании 2LEVEL, в то время как IP отслеживает последова¬
тельность слов внутри lLEVEL. Итак, теперь имеется два ожидаемых возврата: конкретно к сооб¬
щению ”End” в 2LEVEL и к сообщению ’’End” в 3LEVEL. Эти указатели, конечно, содержат адре¬
са CFA в поле параметров слов. Указатель инструкций указывает на следующий CFA слова, кото¬
рое должно быть исполнено. Другие указатели, второй и третий в нашем примере, указывают на
слова, поДлежащие исполнению после возвращения из слова на более низкий уровень. Эти указате¬
ли хранятся в стеке возвратов, что является, конечно, причиной его наименования. На верху стека
возвратов хранится адрес программы в интерпретаторе, которая должна быть исполнена по завер¬
шении выполнения слова, а под ним хранятся адреса, управляющие указателем инструкций и через
него возвратом с одного уровня на другой. Из этого должно быть очевидно, почему ничего нельзя
положить в стек возвратов и оставить там при выходе из слова.
214
Это рассмотрение процесса исполнения показывает общий подход к тому, как происходит выпол¬
нение программы, но остается еще много вопросов без ответа. Как осуществляется управление IP и
стеком возвратов? Как выполняется переход между словами с уровня на уровень? Как производится
передача управления машинной программе и уход из нее? Существуют и другие вопросы, на кото¬
рые почти невозможно ответить, используя только стандартные слова. Мы должны тщательно про¬
следить на примере, как это делается. Объяснения, которые мы дадим, являются типовыми для
большинства версий Форта, но существуют и другие способы выполнения программ. Даже если ваш
Форт отличается от рассматриваемого, мы полагаем, что, если вы поняли метод исполнения, опи¬
санный здесь, вы оцените многие аспекты структуры и функций любой версии Форта. Объяснение,
данное ниже, является сложным и потребует очень тщательного изучения и, вероятно, повторного
чтения. Но мы обсуждаем самое ядро Форта и надеемся, что вы сочтете эту работу важной.
Прежде чем мы рассмотрим пример, мы должны ввести еще несколько указателей и некоторые
программы, написанные в машинных кодах. Если их назначение и функции не будут очевидны при
первом прочтении, вернитесь к их описанию позднее, когда вы будете отслеживать процесс испол¬
нения на примере. Кроме указателя инструкций и стека возвратов мы будем пользоваться двумя
другими указателями, чтобы объяснить, как Форт исполняет откомпилированные слова. Один из
них — указатель слов (который мы будем обозначать WP); он содержит адрес поля параметров
слова в момент начала его исполнения (почему это так, будет понятно позднее). Другой указатель
мы будем называть регистром передачи управления (сокращенно — JP). Он служит для хранения
адреса, куда будет передано управление для исполнения программы в машинных кодах. Запомните,
что IP, WP и JP характеризуют лишь один способ представления того, как осуществляется исполне¬
ние слов в Форте. Эти указатели обычно представляют собой регистры центрального процессора, но
существует много реализаций на разных ЭВМ. Сокращения, которые будут использоваться в даль¬
нейшем, представлены в табл. 15.3.
Таблица 15.3. Указатели и стек возвратов, используемые при выполнении программы Форта
IP = Указатель инструкции
Адрес CFA текущего слова, подлежащего исполнению
WP = Указатель слов
PFA слова в начале обращения
JP = Указатель передачи управления
Адрес, куда должен передать управление процессор
RS = Стек возвратов
Стек адресов возврата
Исполнение в Форте представляет собой последовательность переходов между двумя типами про¬
грамм в машинных кодах. Это:
1. Исполнительная программа слова Форта (т.е. программа, адрес которой указан в поле про¬
граммы).
2. Программы в машинных кодах, размещенные в поле параметров слов-примитивов.
Исполнительная программа определяет то, как выполняется слово Форт, а программы в машин¬
ных кодах слов-примитивов выполняют полезную работу Форт-программы. Различие между испол¬
нительной программой и программами в машинных кодах несколько запутанно, так как исполни¬
тельная программа слова-примитива — это и есть программа в машинных кодах, размещенная в
его поле параметров. Выполнение слов типа двоеточие и примитивов контролируется тремя корот¬
кими программами в машинных кодах, каждая из которых требует только около дюжины байтов на
8-битовом процессоре, NEXT является наиболее фундаментальной программой, так как она исполь¬
зуется для завершения всех программ в машинных кодах используемых Фортом.
NEXT является не словом словаря Форта, а словом, используемым Форт-ассемблером для пере¬
хода к внутреннему или адресному интерпретатору. Машинная программа, к которой происходит
переход при NEXT, осуществляет переход к выполнению следующего слова Форта.
Слова типа двоеточие используют еще две программы, которые мы рассмотрим. Первая — это
исполнительная программа описаний типа двоеточие (иногда называется DOCOL), ее функции за¬
ключаются в управлении исполнением таких описаний.
215
Другим словом, используемым при исполнении слов типа двоеточие, является EXIT, CFA которо¬
го компилируется в конце каждого описания оператором ;. Слово EXIT извлекает из стека возвра¬
тов и заносит в указатель инструкции код, управляющий тем, что будет исполняться следующим.
Как исполнительная программа, так и слово EXIT передают управление слову NEXT, чтобы перей¬
ти к исполнению следующего слова. Функционирование этих программ станет понятным из нашего
примера.
Давайте опишем слово
: SQUARE DUP * ;
и, используя произвольные адреса, рассмотрим его скомпилированную структуру; это позволит нам
исследовать детально механику исполнения программы Форта. Тело слова SQUARE может быть
представлено как
CFA PFA
Тело слова SQUARE 20000 20002 20004 20006
Содержит 13000 18000 17000 12000
(CFA слов) DUP * EXIT
CFA слова SQUARE равно 20000, содержимое которого (13000) является произвольным адресом, ко¬
торый мы выбрали для исполнительной программы слов типа двоеточие. Другие три адреса, ском¬
пилированные в PFA слова SQUARE (18000, 17000 и 12000), являются CFA слов DUP, * и EXIT.
Но так как DUP и * являются примитивами (CFA которых указывают на их PFA), мы можем до¬
полнить нашу диаграмму дополнительным списком адресов:
CFA PFA
Тело слова SQUARE 20000 “20002 20004 20006
Содержит... 13000 18000 17000 12000
(CFA слова) DUP * EXIT
Содержит ... 18002 17002 12002
Чтобы получить более интересный пример, давайте опишем слово
: CUBE DUP SQUARE * ;
теперь мы можем исследовать, как слова типа двоеточие обращаются к другим словам аналогичного
типа. Используя адреса, которые были приняты для слова SQUARE, мы можем получить карту
компиляции слова CUBE:
CFA PFA
Тело слова CUBE 21000~~2l602 21004 21ббб_~21008
Содержит... 13000 18000 20000 17000 12000
(CFA слов) DUP SQUARE * EXIT
Содержит... 18002 13000 17002 12002
Чтобы детально проследить исполнение слов SQUARE и CUBE, основывайтесь на этих картах.
Когда во входном потоке встретится 3 CUBE, интерпретатор текста положит в стек 3, затем най¬
дет в словаре CUBE и оставит в стеке его CFA (21000). Так как Форт находится в режиме исполне¬
ния, это CFA передается оператору EXECUTE, чтобы начать выполнение слова CUBE. Слово
EXECUTE берет CFA из стека и использует его для получения адреса исполнительной программы
CUBE (13000), который заносится в регистр передачи управления (JP). В то же самое время в ука¬
затель слов WP записывается PFA слова CUBE. Исполнение начинается путем передачи управления
по адресу, лежащему в JP, или 13000 (адрес ислрлнительной программы описаний типа двоеточие).
To, что мы только что объяснили, может быть отображено на диаграмме:
До
Операция После
Стек
- ?
Засылка 3 в стек - 3
Стек
- 3
Обнаружение CFA слова CUBE - 3 21000
EXECUTE, который выполняет:
Стек
- 3 21000
Используя CFA CUBE, - 3
определяет адрес исполнительной
программы описаний типа двоеточие
JP =
?
и кладет в JP JP = 13000
WP =
?
Укладку PFA CUBE в WP WP = 21002
JP =
13000
Переход к исполнительной
программе типа двоеточие
216
Прежде чем вы увидите, что делает исполнительная программа, мы должны заметить, что IP со¬
держит адрес во внешнем интерпретаторе (мы его назовем OUTER), который будет восстановлен,
когда исполнение CUBE будет завершено. Ну а теперь о том, что происходит при работе исполни¬
тельной программы описаний типа двоеточие (названной DOCOL). Обратите внимание, что DOCOL
завершается словом NEXT, работа которого отображена на последних нескольких строках диаграм¬
мы. Вы встретите операцию NEXT много раз, пока мы будем изучать этот пример.
Исполнительная программа слов типа двоеточие сначала заносит IP (содержащее адрес слова, ис¬
пользованного внешним интерпретатором) в стек возвратов. Когда CUBE выполнено, исполнение
будет продолжено в OUTER. Слово EXECUTE оставляет PFA слова CUBE в WP, и теперь оно за¬
гружается в IP. Исполнительная программа слов типа двоеточие переходит затем к слову NEXT,
которое выполняет ту же функцию, что и при завершении любой программы-примитива. Так как
IP теперь содержит адрес в поле параметров CUBE, где записан CFA слова
До Операция После
DOCOL, которая выполняет:
IP = OUTER Засылку IP в стек возвратов,
чтобы сохранить адрес,
куда возвращаться
WP = 21002 Занесение PFA CUBE в IP
NEXT, чтобы закончить
исполнение программы :
IP @ @ Извлечение адреса исполнительной
программы слова DUP и загрузку
его в JP
WP = 21002 Занесение PFA DUP в WP
IP = 21002 Приращение IP, теперь он
указывает на следующее
слово, подготовлено его
исполнение
JP = 18002 Передачу управления исполни¬
тельной программе DUP (машин¬
ной программе дублирования
кода в стеке)
Стек - 3 Работу DUP
RS — 0UTER
IP = 21002
JP = 18002
WP = 18002
IP = 21004
3 3
DUP (конкретно 21002), команда
iP @ @
является символическим отражением того, как определяется адрес исполнительной программы DUP.
(Поскольку 21002 @ выдает 18000, содержимое 18000, т.е. 18002, может быть занесено в JP с по¬
мощью команды 21002 @ @. Конечно, @, как это использовано здесь, является символическим и
не означает занесения чего-либо в стек. Если что-то не ясно, смотрите диаграмму CUBE.) Адрес ис¬
полнительной программы DUP (ее PFA, поскольку программа в машинных кодах лежит в ее
До Операция После
IP = 21004 (Словом DUP не изменен) IP = 21004
NEXT, который выполняет:
IP @ @ Вычисление адреса исполнительной
программы SQUARE и загрузку ее в
JP JP
WP = 18002 Занесение PFA SQUARE в WP WP
IP = 21004 Приращение IP, теперь он
указывает не следующее
слово, подготовлено его
исполнение IP = 21006
JP = 18002 Передачу управления исполни¬
тельной программе SQUARE (машин¬
ной программе дублирования кода
в стеке)
13000
20002
\
поле параметров) загружается в JP, так как процессор должен исполнить ее непосредственно. Но
прежде чем это сделано, IP дается приращение, чтобы он указывал на следующую ячейку (21004) в
PFA слова CUBE. Затем управление передается программе DUP, которая дублирует в стек число 3.
217
После того как DUP выполнит свою работу, исполнение переходит к оператору NEXT, который
является всегда последней командой в поле параметров примитива.
Еще раз исполняется программа DOCOL, на этот раз для слова SQUARE.
Программа DUP дублирует число 3, хранящееся в стеке. Заметьте, что хотя в WP загружено PFA
слова DUP, оно не использовано. Исполнение продолукается путем передачи управления оператору
NEXT, так как DUP является примитивом.
До
Операция
После
IP = 20004
(Не изменен словом DUP)
IP = 20004
IP @ @
NEXT, который выполняет:
Определение адреса
исполнительной программы
слова * и загрузку его в JP
JP = 17002
WP = 18002
Занесение PFA * в WP
WP = 17002
IP = 20004
Приращение IP, теперь он
указывает на следующее
слово,
IP = 20006
JP » 17002
подготовлено его исполнение
Передачу управления
Стек = —3 3
исполни-тельной программе *
(машин-ной программе
перемножения двух чисел)
Выполнение *
— 3 9
* перемножает два верхних числа в стеке, и мы
еще раз обращаемся к NEXT, так как * является
примитивом.
До
Операция
После
IP = 20006
(Не изменен словом *)
IP = 20006
IP @ @
NEXT, который выполняет:
Получение адреса
исполнительной программы
слова EXIT и загрузку его
в JP
JP = 12002
WP « 17002
Занесение PFA слова EXIT
в WP
WP = 12002
IP = 20006
Приращение IP, теперь он
указывает на следующее
слово
IP= 20008
(после конца SQUARE)
IP = 20008
IP = 12002
Передачу управления
исполни-тельной программе
EXIT (машин-ной программе
перехода на более высокий уровень)
Заметьте, что хотя, IP указывает на ячейку после конца SQUARE, это не имеет никакого значе¬
ния, когда мы завершаем исполнение SQUARE с помощью EXIT, так как в случае исполнительной
программы типа двоеточие EXIT завершается переходом к NEXT.
До
Операция
После
RS — 0UTER 21006
EXIT, который выполняет:
Засылку в IP кода
из стека
RS — 0UTER
возвратов
IP = 21006
NEXT,KOTOpb^ выполняет:
IP @ @
Определение адреса
исполни-тельной программы
слова * и загрузку его
в JP JP а 17002
WP=12002 Занесение PFA слова *
в WP WP = 17002
IP=20006 Приращение IP, теперь он
указывает на следующее слово,
подготовлено его
исполнение IP=2,0008
JP=17002 Передачу управления
исполни-тельной программе *
218
(машин-ной программе
умножения двух чисел).
Стек = 3 9 Работу * - 27
* умножает два числа в стеке, и мы снова сталкиваемся с командой NEXT в конце программы, на¬
писанной в машинных кодах.
До
Операция
После
IP=21008
(Не изменен словом *)
NEXT, который выполняет.
IP=21008
IP=@ @
Получение адреса
исполнительной программы
слова EXIT и загрузку
его в JP
JP=12002
WP=17002
Занесение PFA слова EXIT в WP
WP=12002
IP=21008
Приращение IP, теперь он
указывает на следующее
IP=21010
слово, после конца слова CUBE
IP=21010
JP=12002
Передачу управления
исполни-тельной программе EXIT
(машин-ной программе перехода
на более высокий уровень)
Здесь снова не имеет значения то, что IP содержит адрес, который следует после описания CUBE.
Теперь мы завершаем исполнение CUBE оператором EXIT.
До
Операция
После
RS=OUTER
Оператор EXIT, который
выполняет
Засылку в IP кода
RS — ?
из стека возвратов
IP = 0UTER
IP* @ @
NEXT, который выполняет
Определение адреса
4
исполнительной программы
слова 0UTER и загрузку его в JP
JP = *>
WP = 12002
Укладку PFA ? в WP
WP = ?
IP = 0UTER
Приращение IP, теперь он
указывает на следующее слово,
подготовлено его
исполнение
IP=0UTER+2
JP = ?
Передачу управления
Стек = 27
исполни-тельной программе
слова во внешнем интерпретаторе
Стек содержит три в кубе
Это завершает исполнение CUBE и управление передается назад к исполнительной программе
следующего слова, которое исполняется внешним интерпретатором. Когда входной поток иссякает,
управление возвращается клавиатуре и печатается отклик ”ок” при коде 27 в стеке.
Исполнение слова CUBE можно в общем виде охарактеризовать как выполнение последователь¬
ности программ в машинных кодах, представленных в табл. 15.4.
Исполнение всех слов Форта организовано одним и тем же основным способом. Единственной ва¬
риацией является действие исполнительных программ, на которые указывает содержимое полей
программы каждого слова. Исполнительная программа для константы занесет в стек содержимое ее
поля параметров и передаст управление слову NEXT. Исполнительная программа слова + (програм¬
ма в машинных кодах лежит в его поле параметров) сложит два числа из стека и перейдет к испол¬
нению слова NEXT. Программы в машинных кодах для слов BRANCH и 7BRANCH должны вычис¬
лять адрес передачи управления и изменять соответственно IP, прежде чем исполнить NEXT. Раз¬
личные другие слова (такие как исполнительные программы для DO и LOOP) изменяют порядок
исполнения программы Форта путем изменения содержимого стека возвратов. Исполнение програм¬
мы в Форте является простым и весьма гибким.
Хотя слово NEXT, различные исполнительные программы и слово EXIT все являются очень ко¬
роткими (и, следовательно, быстрыми), Форт имеет встроенные ’’накладные расходы”, так как пе¬
реход от одной программы к другой через слова NEXT и EXIT требует времени. Несмотря на это,
используя CUBE в качестве примера, в табл. 15.4 показано, что только около половины байтов, ис¬
"219
полняемых словом CUBE, составляют ’’накладные расходы”. Половина потрачена в DUP и *, кото¬
рые были бы нужны, даже если слово CUBE было написано в машинных кодах. Поскольку скорость
исполнения является примерно пропорциональной числу исполняемых байтов (в действительности *
медленнее, так как эта программа включает в себя циклы), слово CUBE, как написано в Форте, не
более чем вдвое медленнее варианта, написанного на ассемблере. Заметьте также, что Форт очень
Таблица 15.4. Программы выполняемые при исполнении слова CUBE 1
Имя
Требуемое число байт
EXECUT
7
D0C0L для CUBE
14
NEXT
12
DUP
5
NEXT
12
D0C0L для SQUARE
14
NEXT
12
DUP
5
NEXT
12
★
70
NEXT
12
EXIT
12
NEXT
12
★
70
NEXT
12
EXIT
12
NEXT
12
(внешний интерпретатор)
Всего
305
эффективно использует память. Хотя CUBE использует всего 305 байтов, описание CUBE и
SQUARE добавляет к словарю только 30 байтов. Более того, описание CUBE в машинных кодах по¬
требовало бы 75 байтов при оптимальном описании * и DUP. Форт-программы могут действительно
потребовать меньше памяти, чем программа в машинных кодах. Даже если не учитывать быстро¬
действие и использование памяти, трудно себе представить более простой, элегантный или удобный
способ связать фрагменты машинных программ, чем метод, реализуемый в Форте.
Одни микропроцессоры подходят лучше для работы Форта, другие меньше, в зависимости от их
стеков и возможностей косвенной адресации. Существуют несколько Форт-систем со словарями, за¬
писанными в ПЗУ (постоянное запоминающее устройство), но разработаны и более впечатляющие
варианты. Так как машинные программы, используемые Фортом, при исполнении весьма коротки,
имеется возможность применения микропрограмм для непосредственной реализации Форт-команд
вместо стандартного набора инструкций. Если NEXT, различные исполнительные программы и
EXIT можно было бы исполнять за один машинный цикл, то оценка показывает, что скорость ис¬
полнения Форта увеличилась бы более чем в 100 раз по сравнению с написанием Форт-примитивов
на ассемблере для процессора той же серии. Это бы позволило микроЭВМ, ориентированным на
Форт, работать со скоростью больших вычислительных машин.
Не важно, каким процессором вы располагаете, в любом случае вы можете ускорить исполнение
Форт-программ, переписав слова с использованием Форт-ассемблера. С помощью ассемблерного
слова CODE можно описать слова со структурой, идентичной примитивам, с той же легкостью, что
и в случае описания типа двоеточие (предполагается, что вы владеете программированием на ас¬
семблере). Этой темы мы касаемся в гл. 16.
Упражнения
1. Чтобы отображать содержимое стека возвратов (используя слово : XX R@ U. ;), опишите новые версии lLEVEL,
2LEVEL и 3LEVEL. Что в действительности означают отображенные адреса?
2. Что случится, если вы используете команду R> DROP после сообщения в lLEVEL ?
1 В таблице представлены исполняемые байты памяти для типов 8-битовой реализации Форта.
Подчеркнутые программы выполняют полезную работу
220
3. Испробуйте команду R> DROP в других точках lLEVEL и 2LEVEL, чтобы познакомиться с ее действием. Можете вы
объяснить, почему было бы неразумно использовать R> DROP в 3LEVEL?
4. Как может быть использован указатель WP в исполнительной программе для констант ?
5. Опишите CUBE? как
: CUBE?? DUP SQUARE EXIT * ;
и отследите состояние стека, стека возвратов, IP и WP, используя программу CUBE в качестве путеводителя. Является ли
слово EXIT, когда оно используется, в точности таким же, что и в случае, когда оно встречается в конце описания типа дво¬
еточие?
6. Вспомните, что LITERAL компилирует CFA своей исполнительной программы (часто называемой LIT) и следующее за
ним число, лежащее в стеке, в любое описание, где оно использовано. Как вы полагаете, избегает Форт "исполнения" числа,
скомпилированного после LIT, когда слова, где они использованы, исполняются?
Рекурсия
В гл. 8 мы обещали рассказать вам о другом типе циклов — рекурсивных циклах. Рекурсия в вы¬
числительной терминологии означает возможность подпрограмме обращаться к самой себе. В Форте
это значит, что слово обращается к самому себе в пределах своего описания. Поскольку рекурсия
весьма опасна, если вы не понимаете точно, что происходит, мы отложили эту тему до тех пор, по¬
ка не поняли процесса исполнения. Рекурсия в Форте весьма проста в реализации. Все, что требу¬
ется сделать, — это скомпилировать CFA слова внутри его собственного поля параметров. Когда это
CFA встретится, исполнение переключается назад к началу описания, т.е. к началу поля парамет¬
ров. В MMSFORTH и многих других версиях слово, которое это реализует, имеет имя MYSELF.
Описание в MMSFORTH имеет вид
MYSELF ?СОМР LAST-CFA , , IMMEDIATE
LAST-CFA заносит в стек CFA заголовка слова, описанного последним, в данном случае CFA само¬
го слова. Некоторые версии используют RECURSE вместо MYSELF, другие позволяют использовать
просто имя самого слова.
Причина, почему рекурсия опасна, связана с тем, что очень легко создать бесконечные циклы и
довольно легко переполнить стек возвратов. Рассмотрим это:
: DESTROY! MYSELF ,
Когда DESTROY! исполняется, каждый раз при обращении слова к самому себе происходят при¬
ращение указателя стека возвратов и запись в него адреса слова, следующего за словом MYSELF, в
данном случае EXIT. Здесь не только нет способа выхода из цикла, но приращение указателя стека
возвратов очень скоро исчерпает ресурс места в памяти, выделенной для этого. Слово MYSELF в
действительности всегда используется внутри структур управления, например в конструкциях
IF...ELSE THEN..
Если вы опишете
. MYLOOP DUP 10 U< IF DUP 1+ MYSELF THEN ;
и затем напечатаете
5 MYL00P
вы увидите
5 6 7 8 9
С другой стороны, если вы опишете
: 0DDL00P DUP 10 U< IF DUP 1+ MYSELF THEN ;
его исполнение выдаст на экран
10 9 8 7 6 5
Что случилось? Каждый раз, когда ODDLOOP обращается к самому себе, новое число на 1 боль¬
ше, чем предшествующее, заносится в стек. И каждый раз увеличивается указатель стека возвра¬
тов, указывая на . .Когда достигается предел, равный 10, стек возвратов очищается последователь¬
ным исполнением слова . , при этом печатаются числа, записанные в стеке параметров. Вот более
полезный пример. Факториал числа — это результат умнежения числа на число меньше этого на 1
с последующим умножением произведения на число, еще раз уменьшенное на 1, и т.д. To есть фак¬
ториал 5 должен быть равен
5x4x3x2x1
221
Теперь, если вы опишете
: FACTORIAL DUP 1- DUP 2 > IF MYSELF THEN * ;
Тоща
7 FACTORIAL
отобразит 5040, факториал 7. Если это вас смущает, рассмотрим слово
: SHOWFACT DUP . DUP 1- DUP 2 > IF MYSELF THEN DUP . * ; ,
которое при исполнении с 7 в стеке отобразит на дисплее
7 6 5 4 3 26 24 120 720
и, если вы затем напечатаете . , вы увидите значение факториала 5040. Небольшое исследование
SHOWFACT должно прояснить вам, как работает FACTORIAL. Вы можете описать аналогичное
слово с использованием do-loop, но оно будет более неуклюжим. В упражнениях вы найдете еще
несколько приложений MYSELF.
Упражнения
1. Опишите FACTORIAL, используя DO-LOOP.
2. Опишите SHOWASCII так, что, когда вы напечатаете
50 80 SH0WASCII
вы увидите числа и эквивалентные им ASCII-символы.
Сделайте это с использованием рекурсии и do-loop.
3. Опишите слово GENERATIONS для вычисления числа поколений, необходимых одной бактерии, чтобы произвести бо¬
лее 2000 бактерий путем простого деления. Сделайте это, используя рекурсию и конструкцию BEGIN... .
Эти упражнения показывают, что в большинстве случаев циклы предпочтительнее рекурсии.
Выводы
Не существует другого такого языка, в котором введение могло бы поднять ваш уровень понима¬
ния того, как в действительности он работает. И нет другого языка, который бы позволил вам на¬
столько вмешиваться во внутреннюю работу его интерпретатора, компилятора и в способ исполне¬
ния программы. Если вы поняли содержание главы, а также то, как работает ваш микропроцессор,
то вы уже почти в состоянии создать свою версию Форта. Может быть, эта способность языка созда¬
вать и модифицировать самого себя объясняет, почему существует так много коммерчески доступ¬
ных версий Форта. Но, конечно, власть, которую предоставляет вам Форт над самим собой, несет в
себе и определенную ответственность. Очень легко сделать что-то, что разрушит систему. Забудьте
лишь использовать EXIT в слове без заголовка или не обеспечьте выход из рекурсивного слова и вы
будете вынуждены перезагрузить ЭВМ.
Но существует даже более элементарный уровень Форта. Это использование программ на ассемб¬
лере в качестве части Форт-программы. Программирование на ассемблере в Форте так же просто и
интерактивно, как и программирование на Форте, если вы знаете, как работает ассемблер. В гл. 16
мы покажем вам, как вы можете описать такие фундаментальные слова, как SWAP и DROP, опи¬
сывая их в рамках Форта. Вы увидите, что можете использовать Форт для того, чтобы реализовать
все возможности вашей ЭВМ.
Глава 16
ПРОГРАММИРОВАНИЕ НА ФОРТ-АССЕМБЛЕРЕ
Как описано в гл. 3, память ЭВМ представляет собой серию ключей, которые могут использоваться для представления ” 1"
и ”0” в двоичных числах. Программы и данные запоминаются в двоичной форме, таким образом представляя числа, тексг
или машинные коды. В гл. 3 рассмотрены различные способы представления чисел, а в гл. 9 обсуждается запоминание и ма¬
нипулирование строками текста. B этой главе мы обсудим, как писать слова, чтобы исполнять программы в машинных ко¬
дах. Машинный язык (называемый также машинным кодом) — это набор двоичных чисел, которые ЭВМ может интерпрети¬
ровать как инструкции и непосредственно исполнять. Это наиболее фундаментальный способ программирования ЭВМ. Пре¬
имущество использования программы в машинных кодах, в противоположность программе на языке высокого уровня, заклю¬
чается в том, что это дает возможность полностью управлять всеми аспектами работы ЭВМ и позволяет написать программу,
которая будет исполняться так быстро, как только может работать машина. Наиболее общепринятый способ написания про¬
грамм в машинных кодах заключается в использовании программы, называемой ассемблером. Каждая машинная инструкция
имеет имя, называемое мнемоническим, чтобы облегчить ее запоминание. ’’Типичный” (не Форт) ассемблер воспринимает
файл мнемоники, написанный с использованием редактора, и преобразует мнемонику в машинные коды, которые либо запо¬
минаются в другом файле, либо загружаются в память, чтобы быть исполненными. После нескольких последовательных, тре¬
бующих времени и памяти шагов (таких как редактирование связей и загрузка) программа в машинных кодах может быи>
исполнена либо сама, либо совместно с другой программой, написанной на другом языке. Форт-ассемблер использовать мно¬
го проще, так как не требуется редактирования связей и загрузки, а мнемоника может быть использована как часть описа¬
ний слов Форта в Форт-программе. Мнемоника Форт-ассемблера — это слова, которые компилируют машинные коды в сло¬
во, описанное с помощью слова CODE. Эти слова используются так же, как и любые другие слова Форга, за исключением
того, что они исполняются так быстро, как только может ЭВМ. Хотя большинство Форт-программ не требуют никакою зна¬
ния ассемблера, многие из них могут быть значительно ускорены с применением ассемблерной мнемоники в определенных,
критических по времени словах. Большинство обычных книг по Форту лишь немногим более чем упоминают о машинных
программах и о программировании на ассемблере, и для этого есть причины. Каждый тип микропроцессора имеет спой соб¬
ственный набор машинных команд и собственную мнемонику, так что нет способа, написав книгу разумного объема, охва-
гить все типы ЭВМ. Но мы не можем игнорировать этот предмет. Возможность описать слова Форта, способные выполншь
программы в машинных кодах, слишком важна. Мы можем научить вас использовать мнемонику ассемблера так, чтобы на¬
писать машинную программу как часть Форт-программы, но мы должны просить вас выполнить некоторую дополнительную
работу. Вам нужна книга, описывающая ассемблер, который поставлен на вашей микроЭВМ, и вы должны изучигь ee совме¬
стно с тем, что написали мы. Если ваш Форт не имеет ассемблера (хотя он должен его иметь), вы сможете написагь его по¬
сле прочтения этой главы, хотя это и не главная наша задача. B первом разделе упоминаются несколько тем, связанных с
гем, как работает ЭВМ и как это взаимодействует с машинными программами, а затем описывается, как машинная програм¬
ма компилируется в слово Форта. Это может быть сделано даже без ассемблера.
Машинная программа
Сердце микроЭВМ — это центральный процессор (ЦП), обычно один кристалл, называемый
микропроцессором. ЭВМ управляется путем загрузки двоичных кодов в элементы памяти самого
ЦП (в противоположность основной памяти), которые называются регистрами. Регистры использу¬
ются либо для специальных, либо для общих целей. Некоторые главным для хранения чисел во
время вычислений, в то время как другие можно использовать только для того, чтобы запоминать
адрес следующей исполняемой инструкции, или для хранения текущего адреса верхней ячейки сте-
ка. За подобными справками вам следует обращаться к руководству по вашему процессору.
В 8-битовом процессоре 8 бит (байт) рассматриваются в качестве команд или данных. Хотя сущест¬
вует только 256 (28) возможных комбинаций из 8 битов, реализуемо более 256 типов команд, так
как некоторые байты будут заставлять ЦП воспринимать еще один или несколько байтов для фор¬
мирования команды. Например, 8-битовый микропроцессор Z-80 имеет более 700 типов команд, со¬
держащих от одного до 4 байтов каждая. Шестнадцатибитовые микропроцессоры могут использо¬
вать 8- или 16-битовые команды, 8- или 16-битовые регистры и могут передавать данные по 16 или
32 битов за такт в зависимости от модели. Конечно, микроЭВМ имеют даже больше вариаций. Мы
приведем примеры для микропроцессора Z-80, так как он используется в TRS-80 и в семействс
СР/М машин, а также для 8086 и 8088, так как они применяются в IBM PC и семействе оборудова¬
ния, использующем MS-DOS. Самый простой способ рассмотрения машинных программ на вашей
223
ЭВМ — это пропечатка поля параметров примитивов. Используя описание DUMP из гл. 14,
’ SWAP 1 DUMP
в Форт-79 или
* SWAP BODY 1 DUMP
в Форт-83, отобразим машинную программу, которая меняет местами два верхних элемента в сте¬
ке. Для Форта Z-80, который использует регистр указателя стека параметров Форта, эта програм¬
ма, вероятно, отпечатает
(адр) D1 E1 D5 E5 FD E9 xx xx xx xx xx xx xx xx xx xx
где мы использовали ”хх” для обозначения байтов, величина которых не определена. (Это пример
для MMSFORTH на TRS80.) Но командные байты ЭВМ (известные так же как коды операций) ни-
чего ^е значат, если у вас нет книги по программированию на ассемблере для Z-80, с помощью ко¬
торой можно декодировать их. Если бы вы рассмотрели каждый байт, то поняли бы, что
D1 = POP DE
E1 = POP' HL
D5 = PUSH DE
E5 = PUSH HL
FD E9 = JP (IY)
но это вряд ли прояснит, что здесь происходит, если вы не знаете ассемблера Z-80. Различные ко¬
манды (POP DE и т.д.) являются мнемоникой, о которой мы говорили. Если вы знаете смысл этой
мнемоники, вы можете оценить, как работает слово SWAP.
D1 = POP DE Перенести число из стека в регистр DE
E1 = POP HL Перенести число из стека в регистр HL
D5 = PUSH DE Перенести число из регистра DE в стек
E5 = PUSH HL Перенести число из регистра HL в стек
FD E9 = JP (IY) Передать управление по адресу, лежащему в регистре IY
Программисту, работающему на Форте, довольно легко понять команды PUSH и POP, так как ’’ап¬
паратный стек”, реализованный в Z-80 с помощью SP-регистра (указателя стека), является в боль¬
шинстве версий Форта как раз тем самым стеком, который непосредственно использует Форт. При¬
менены здесь два регистра общего назначения с именами DE и HL, а также команда записи чисел в
стек, названная PUSH, и извлечения кода из стека, называемая POP. Машинная программа для
SWAP просто говорит ЦП Z-80 убрать два числа из стека и записать их в два регистра ЦП, а затем
положить их в стек в обратном порядке. Это показывает, насколько легко переслать любое число
кодов в регистры микропроцессора и обратно, используя стек. Команда JP (IY) (которая может
быть другой в вашем Форте, даже если у вас применена система Z-80) заставляет Z-80 передать уп¬
равление по адресу, записанному в регистре IY. Это инструкция, названная нами NEXT в гл. 15.
Инструкция NEXT используется для завершения исполнения Форт-программы в машинных кодах
(примитива) и передачи управления внутреннему интерпретатору Форта. Коды операций для NEXT
в вашем Форте могут быть другими, даже если вы используете Z-S0. NEXT в действительности сло¬
во MMSFORTH и не является стандартным словом, но почти все версии Форта содержат его экви¬
валент. Чтобы проиллюстрировать, как одно и то же задание реализуется на разных микропроцес¬
сорах (так вы не будете разочарованы, если не имеете оборудования, которое мы обсуждаем), нижс
представлены программы для SWAP в двух различных Фортах для 16-битовой IBM PC, базирую¬
щейся на микропроцессоре 8088.
MMSFORTH
5A = POP DX
58 = POP АХ
52 = PUSH DX
50 = PUSH АХ
AD = LODS АХ. SI Загрузка в регистр АХ содержимого регистра SI
93 = XCHG АХ, BX Пересылка содержимого BX в АХ и наоборот
FF 27 = JMP.(BX) Передача управления по адресу, лежащему в регистре BX
MVPFORTH
5B = POP BX
58 = POP АХ
53 = PUSH BX
50 = PUSH АХ
AD = L0DS АХ, SI Загрузка в регистр АХ содержимого регистра SI
224
AB D8 = MOV BX, АХ Пересылка содержимого АХ в регистр BX
FF £7 = JMP (BX) Передача управления по адресу, лежащему в регистре BX
Вы можете видеть, что так же, как в версии MMSFORTH на Z-80, оба эти описания SWAP из¬
влекают и укладывают коды из стека и в стек с использованием регистров. MMSFORTH применяет
АХ- и DX-регистры, а MVPFORTH — АХ и BX. Следующие три байта в MMSFORTH и следующие
четыре в MVPFORTH являются двумя версиями NEXT. Обе загружают содержимое регистра SI в
ВХ-регистр и затем для продолжения работы осуществляют передачу управления по адресу, храня¬
щемуся в BX. В обеих версиях Форта SI-регистр используется для хранения указателя инструкций,
который мы в гл. 15 назвали IP. Существуют ли какие-то причины для различий между
MMSFORTH и MVPFORTH? Версия MVPFORTH немного быстрее, в то время как NEXT
MMSFORTH требует на один байт меньше памяти; различие тривиально. (Но быстродействие очень
важно в определении слова NEXT, поскольку оно завершает все примитивы.) Версия SWAP
MVPFORTH может точно так же использоваться в MMSFORTH, и наоборот, с точно таким же ре¬
зультатом. Существует обычно несколько различйых путей реализации одной и той же функции в
машинных кодах. Прежде чем создавать слово, которое выполняет машинную программу, давайте
рассмотрим откомпилированную форму программы-примитива. Поле программы слова-примитива
указывает на его поле параметров. Как мы видели в гл. 15, это придает примитиву следующую
форму:
Поле Имени Связи Программы Параметров
Адрес NFA LFA CFA PFA
Содержимое Имя Адрес PFA (Машинная программа NEXT)
Для того чтобы вы могли написать собственный примитив, вы должны найти машинную програм¬
му NEXT или ее эквивалент в вашей системе. Это можно сделать с помощью пропечатки некото¬
рых примитивов, чтобы узнать, как они завершаются. Если вы располагаете Форт-ассемблером, то
он, вероятно, снабжен словом, подобным NEXT, которое выполняет эту функцию. Теперь давайте
опишем новую версию SWAP с именем MYSWAP. Начнем с формирования заголовка MYSWAP, а
затем заменим содержимое CFA так, чтобы там лежал адрес поля параметров MYSWAP. Чтобы
сформировать заголовок MYSWAP напишем
CREATE MYSWAP HERE DUP 2- !
Его PFA определяется с помощью слова HERE и затем эта величина записывается в поле програм¬
мы MYSWAP, которое занимает два байта перед PFA. (Эта методика может не работать для Форта,
который записывает слова нетрадиционным способом.) Раз вы поместили адрес поля параметров
MYSWAP в его CFA, компилируйте далее машинную программу просто байт за байтом (убедитесь,
что вы работаете с шестнадцатеричной системой):
D1 С, E1 С, D5 С, E5 С, FD С, E9 С,
для MMSFORTH на Z-80 (или другие байты, используемые в описании SWAP в вашей системе). Не
забудьте включить машинную программу, которая осуществляет возврат к Форту. В MMSFORTH
вы могли бы использовать слово NEXT вместо байтов FD и E9. Вы можете, конечно, применить эк¬
вивалентное слово из вашей версии. Если MYSWAP делает то же, что и SWAP, то открыт путь для
описания аналогичным способом остальных слов-примитивов. Если MYSWAP не работает (что ма¬
ловероятно), распечатайте ваше описание, чтобы убедиться, что содержимое CFA указывает на PFA
и что машинная программа соответствует откомпилированному тексту SWAP, и затем испытайте
описание еще раз. Если вы собираетесь описать более чем пару слов-примитивов, вы, возможно, за¬
хотите для формирования заголовков описать
CODEHEAD CREATE HERE DUP 2- ! ,
Форма обращения
CODEHEAD MYSWAP (машинная программа. NEXT)
CODEHEAD работает во многом аналогично слову CODE из ASSEMBLER, описанному в следую¬
щем разделе.
Если вы намереваетесь написать много слов-примитивов, используя эту методику или ассемблер,
имеется пара вопросов, которые вы должны решить. Первый: использует ли ваш Форт аппаратный
указатель стека (указатель стека, с которым работает микропроцессор) для хранения адреса стека
параметров? Большинство версий Форта используют. Это может быть определено путем пропечатки
SWAP, DUP или OVER, чтобы выяснить, как они определены. Если они используют PUSH и (или)
РОР-команды, как определено для вашего микропроцессора, тогда Форт-стек и аппаратный стек
совпадают. Второй: какой регистр используется Фортом в качестве указателя инструкции (который
мы в гл. 15 назвали IP)? Пропечатайте машинную программу для EXIT, чтобы выяснить, в какой
регистр заносится содержимое стека возвратов. Важно сохранить содержимое этого регистра, если
8 М. Келли
225
вы должны использовать его для своих целей, и принять меры для того, чтобы восстановить его ис¬
ходное значение, когда машинная программа будет завершена. Указатель слов Форта (WP) можст
быть, вероятно, изменен вашей программой-примитивом, так как он, вернее всего, будет измснсн
сразу после NEXT, когда исполнение вашего слова будет завершено. Полезно также знать, какой
регистр используется для хранения WP, так как он будет всегда содержать CFA или PFA вашем
программы-примитива, когда к ней произошло обращение. Заметьте, что существует много вариан¬
тов написания Форта, поэтому использование регистров и стеков должно быть известно для вашей
системы, прежде чем вы сможете уверенно программировать слова-примитивы. К сожалению, неко¬
торые поставщики Форта не сообщают вам об использовании внутренних регистров в документа¬
ции. Таким образом, написание Форт-программ в машинных кодах содержит некоторое число до¬
садных деталей, за которыми вы должны следить. Несмотря на это, машинные программы могут
быть введены в Форт-программу весьма легко — вам нужно только внимательно следить за
применением в Форте определенных регистров, особенно того, который используется в качествс
указателя инструкции (IP). Если для вашей программы-примитива нужны все регистры, вы може¬
те, конечно, запомнить значение IP в стеке (или в переменной) вплоть до момента занесения его в
соответствующий регистр перед завершением работы вашей программы. Мы обсудим некоторые
другие вопросы, которые требуют внимания, в следующем разделе. Даже с учетом этих досадных
обстоятельств интерфейс между языком высокого уровня и машинной программой намного проще
реализовать в Форте, чем в любом другом языке.
Упражнения
Ответы для упражнений этой главы приведены для микропроцессоров Z-80 и 8088, но вы, вероятно, захотите выполиии»
их также на'вашей собственной ЭВМ, если у вас другой ЦП. В этом случае проверьте результат путем сравнения вашего ре¬
шения с пропечаткой поля параметров описания в вашем Форте там, где это возможно.
1. Предложите программы-примитивы и мнемонику для Z-80 и 8088 для стековых операндов. В этих упражнениях ис¬
пользуйте только PUSH, POP и NEXT.
а) DROP; б) DUP; в) OVER; г) ROT.
2. Версия F83 Лаксена и Перри имеет несколько нестандартных слов для работы со стеком, которые вы, возможно, захо¬
тите использовать. Одно из них, TUCK, действие которого заключается в ’’подсовывании” копии верхнего элемеша стека
под второй элемент сверху (nl n2 — n2 nl n2), является оператором, обратным по отношению к OVER. Опишите TUCK в
виде программы-примитива для Z-80 и 8088, используя методику, описанную для MYSWAP.
3. NIP является еще одним нестандартным словом F83, используемым для удаления второго элемента из стека (nl n2 --
n2). Опишите его для Z-80 и 8088.
4. В Z-80 команда EX [SP], HL меняет местами верхний элемент стека (содержимое адреса, на который указывает ре¬
гистр SP) и содержимое регистра HL. Опишите ROT. Как может быть сделано более эффективным описанное выше SWAP
(использовать меньшее число байтов или обеспечить исполнение за меньшее число циклов), если применить эту команду со¬
вместно с PUSH и POP?
5. -ROT является оператором, инверсным по отношению к ROT (или тем же, что ROT ROT), и заносит верхний элемеш
стека в третью позицию сверху (nl n2 n3 — n3 nl n2). Опишите его для Z-80 и 8088.
6. Как может быть сделано более эффективным ваше описание DROP путем оперирования адресом в указателе стека, не
используя POP?
Форт-ассемблеры
Теперь ясно, что машинные программы могут быть встроены в слова Форта методом, использо¬
ванным при описании MYSWAP. Итак, в чем заключается задача ассемблера? Просто в том, чтобы
сделать это более легким. Форт-ассемблер состоит из мнемоники, которая компилирует машинные
команды и применение которой намного проще, чем компиляция байтов с помощью C,. Ассемблер
может также включать в себя другие слова, которые не являются мнемокодами, а служат для орга¬
низации циклов и условных переходов.
Но существуют только четыре слова ассемблера, которые специфированы стандартами:
ASSE^iBLER, имя словаря ассемблера (смотри гл. 14); CODE, которое открывает описание слова в
ассемблере и является аналогом :; END-CODE, которое подобно ; завершает описание, и ;CODE,
которое действует по аналогии с DOES>, позволяя использовать мнемонику ассемблера для опреде¬
ления поведения производных слов, полученных с помощью слбв-описателей, на фазе исполнения
(смотри гл. 10). Причина для ограниченности списка стандартных слов заключается в том, что коды
226
операций и соответствующая им мнемоника варьируются от процессора к процессору. Другие слова
ассемблера, например позволяющие организовать циклы и ветвление программы, сильно варьиру¬
ются от версии к версии и не стандартизованы. Это делает трудным дать полное и общее описание
ассемблера. Мы опишем некоторые аспекты MMSFORTH ассемблеров Z-80 и 8088, но мы рассчиты¬
ваем на вас в случае использования этого описания для других процессоров и версий Форта. В зави¬
симости от версии Форт-ассемблер может быть либо полным (способным компилировать все коды
операций данного микропроцессора), либо частичным (включающим только наиболее часто исполь¬
зуемые функции). В последнем случае коды операций, не генерируемые ассемблером, могут быть
откомпилированы непосредственно с помощью С, точно так же, как выше компилировалось слово
MYSWAP (пример приведен ниже).
Форт-ассемблер описан как словарь с именем ASSEMBLER и, как и в случае любых других кон¬
текстовых словарей, команда
ASSEMBLER DEFINITIONS
используется, чтобы добавить новые ассемблерные слова в словарь ASSEMBLER. Имея отдельный
словарь ASSEMBLER, можно использовать в ассемблере любые имена (например, слова мнемоники
и условных переходов), не боясь конфликтов со словами в Форте или другом словаре.
Словом, открывающим ассемблерное описание, является CODE. Оно формирует заголовок слова-
примитива, делает словарь ASSEMBLER контекстным, записывает в CFA нового слова адрес его
PFA и машинную программу, которая там должна храниться. Его описание очень похоже на слова,
которые мы использовали для формирования заголовка MYSWAP.
CODE CREATE HERE DUP 2- ' [COMPILE] ASSEMBLER ,
где [COMPILE] необходимо в случае, если в вашем Форте имя словаря является словом немедлен¬
ного исполнения.
Вот пример описания MYSWAP с использованием MMSFORTH на процессоре 8088 IBM PC. Опи¬
сание для любой версии Форта на микропроцессорах 8088 или 8086 будут сходными:
CODE MYSWAP ( n1 n2 — n2 n1) DX POP АХ POP DX PUSH АХ PUSH NEXT END-
CODE
Запомните, что вам нужно использовать что-то еще вместо NEXT или, может быть, NEXT будет
частью вашего Форт-описания END-CODE. Применение мнемоники здесь очевидно. Заметьте, если
вы используете обычные ассемблеры, то при записи слов применяется типичная для Форта нотация.
Вместо записи POP DX, для того чтобы перенести верхний код из стека в DX-регистр, Форт ис¬
пользует DX POP. Позднее вы увидите почему.
Если бы вы пропечатали описание MYSWAP, то вы бы увидели, что оно идентично SWAP. Вы
уже знаете, что NEXT используется для передачи управления внутреннему интерпретатору. Тогда
что же делает END-CODE? В некоторых ассемблерах END-CODE может выполнять функцию
NEXT. В других, таких как MMSFORTH, единственная функция END-CODE — это сделать кон¬
текстным тот словарь, который им был до начала работы CODE. Стандарты требуют, чтобы END-
CODE сделал находимым в словаре имя слова, созданного CODE. (Если при компиляции выявлена
ошибка, то END-CODE этого не сделает, предотвращая тем самым узнавание ошибочного слова.)
END-CODE осуществляет это обычно путем установления бита-метки в соответствующее состояние
(смотри гл. 14). Некоторые версии Форта, такие как MVPFORTH, используют также ;C в качестве
синонима END-CODE.
В описании MYSWAP мнемоника скомпилировала соответствующий объектный код в поле пара¬
метров слова. Таким образом DX POP заносит код 5A в качестве цервого байта в поле параметров.
Вы можете сформировать тот же самый объектный код в MMSFORTH (и во многие другие версии
Форта), если вы введете
CREATE MYSWAP HERE DUP 2- 1 ASSEMBLER
DX POP АХ POP DX PUSH АХ PUSH NEXT FORTH
Вы можете также использовать
CREATE MYSWAP HERE DUP 2- 1 .ASSEMBLER
DX POP АХ POP 52 С, 50 С, NEXT FORTH
ИЛИ
CODE MYSWAP DX POP АХ POP 52 С, 50 С, NEXT END-CODE
Теперь вы можете увидеть, как можно ввести объектный код в описание CODE, даже если у вас
нет полного набора мнемоники.
Могут существовать и другие способы завершения описаний типа CODE. В MMSFORTH следую¬
щее описание MYSWAP является идентичным по своему поведению нашему первому описанию:
C0DE MYSWAP DX POP АХ POP PSH2 END-CODE
227
PSH2 эквивалентно следующему:
DX PUSH АХ PUSH NEXT
Если у вас нет PSH2, которое является удобным словом, так как часто нужно засылать содержимое
этих двух регистров в стек, вы можете описать его:
ASSEMBLER DEFINITIONS
. PSH2 DX PUSH АХ PUSH NEXT ; FORTH
Это должно подсказать вам мысль, как можно добавлять к ассемблеру новую мнемонику и новые
слова.
Давайте рассмотрим еще одно описание, которое представляет собой еще один пример и послу¬
жит расширению вашего опыта в этой области:
CODE + АХ POP DX POP DX- АХ ADD АХ PUSH NEXT END-CODE
При операции + два числа извлекаются из стека и засылаются в АХ- и DX-регистры. Слово ADD
складывает содержимое АХ- и DX-регистров (результат заносится в АХ). Наконец сумма из АХ за¬
сылается в стек. Имеется, конечно, еще много мнемонических кодов, которые мы здесь используем.
Как мы сказали раньше, вам следует изучить книгу по программированию на ассемблере, чтобы
вполне освоить Форт-ассемблер.
Действие слова ;CODE сопоставимо с DOES>. Подобно DOES>, ;CODE отмечает начало исполня¬
емой части программы слова-описателя, но рабочая программа производного слова в этом случае
будет написана в машинных кодах с использованием мнемоники ассемблера или C,. Таким образом,
описания
■ FORTH-CARRAY CREATE 1+ ALLOT
DOES> + .
И
CODE-CARRAY'CREATE 1+ ALLOT
, CODE HL POP DE HL ADD HL PUSH NEXT END-CODE
эквивалентны в ассемблере для Z-80, но слова, сформированные CODE-CARRAY, будут исполнять¬
ся намного быстрее. На IBM PC в MMSFORTH 100.000 итераций описания DOES> требуют 7,2 с, в
то время как эквивалентные им описания ;CODE выполнят эту работы за 2,3 с.
В отличие от слова DOES>, которое засылает в стек PFA производного слова (и используется
производными словами FORTH-CARRAY для вычисления адреса элемента массива), ;CODE требу¬
ет, чтобы адрес лежал в ячейке, следующей за ним. Адрес PFA или жестко связанного с ним адреса
содержится в регистре, который используется в качестве указателя слов (WP). Предшествующее
описание CODE-CARRAY предполагает, что WP хранится в регистре DE, как это и есть в
MMSFORTH для TRS-80. В MMSFORTH для IBM PC WP содержится в регистре BX и указывает на
CFA (на 2 меньше, чем PFA); описание имеет вид
8O88-CODE-CARRAY CREATE 1+ ALL0T
; C0DE АХ P0P BX АХ ADD 2 П АХ ADD AX,PUSH NEXT END-C0DE
Вы должны будете определить, как найти и использовать WP в вашей собственной версии Форта.
Упражнения
1. Сформируйте CODE-описания для:
а) DUP б) OVER в) ROT г) 2DUP д) TUCK e) NIP ж) -ROT, используя мнемонику Z-80 и 8088.
2. Слово MMSFORTH-ассемблера PSH эквивалентно PSH2, за исключением того, что оно засылает в стек только содер¬
жимое регистра АХ. Опишите PSH.
3. Опишите слово PSH3, которое должно работать как PSH2, но заносить в стек содержимое регистров BX, DX и ЛХ в
указанном порядке.
4. Опишите слово ;C так, чтобы оно выполняло
NEXT END-C0DE
5. Опишите слово типа CODE с именем @REGS, которое заносит в стек 8088 содержимое регистров DI, SI, SP, DX, CX,
BX и AX в указанном порядке. Теперь опишите елово типа двоеточие .REGS, которое использует @REGS и отображает со¬
держимое регистров микропроцессора 8088.
6. Мнемокод микропроцессора 8088 SUB вычитает содержимое одного регистра из содержимого другого. Если SUB заме¬
нит ADD в описании слова +, приведенном выше, содержимое регистра DX будет вычитаться из содержимого регистра AX
(результат останется в AX). Опишите слово -.
7. Используя ;CODE, опишите следующие слова:
а) ARRAY б) DARRAY в) CONSTANT г) 2CONSTANT
228
Как работает ассемблер
Нам следует разобраться с тем, как работает ассемблер, с целью знакомства с техникой програм¬
мирования, а также и потому, что вы можете захотеть расширить возможности и список мнемоко¬
дов в вашем ассемблере. Проще всего описать слово ассемблера NEXT. На TRS-80 в MMSFORTH
это описание выглядит как
ASSEMBLER DEFINITIONS
: NEXT ( --) FD С. E9 С, , FORTH
в то время как на IBM PC
ASSEMBLER DEFINITIONS
NEXT ( —) AD С, 93 С. FF С, 27, С, ; FORTH
Вам следует понимать, как это работает. Конечно, в вашей версии Форта это может быть и по-дру-
гому.
Мнемоника ассемблера немного сложнее, но прямолинейнее. Как мы видели, мнемоника — это
просто слова, которые записывают одну или более машинных команд в тело слова CODE. Некото¬
рые мнемокоды работают сами по себе, другие требуют аргументов, таких как имена регистров, для
того чтобы скомпилировать соответствующий машинный код. Описание мнемоники может быть
простым или сложным, в зависимости от числа необходимых аргументов и от количества машинных
команд, которые она должна скомпилировать. Команда RET (возвращение из подпрограммы) для
8088 является однобайтовой инструкцией, не требует аргументов и может быть описана простым
мнемокодом. Она описывается как
RET ( —) C3 С. ;
Но поскольку существует больше^ число мнемоник с однозначным соответствием между мнемоко¬
дом и однобайтовой инструкцией без каких-либо аргументов, для формирования такой мнемоники
лучше иметь слово-описатель
■ OARGMAKE CREATE С. D0ES> C@ С, ;
После этого можно сформировать таблицу мнемоники следующим образом:
СЗ OARGMAKE RET СЕ OARGMAKE INT0
90 OARGMAKE NOP CF OARGMAKE IRET
9C OARGMAKE PUSHF 9D OARGMAKE POPF
И т. д.
Мнемоника ассемблера, требующая одного аргумента, немного сложнее. К счастью, в конструк¬
ции машинных команд существует логика. Для микропроцессора 8088 большинство мнемокодов, ко¬
торые используют в качестве аргумента номер только одного регистра, формируют машинную инст¬
рукцию, где номер регистра закодирован в младших трех разрядах. Таким образом, в двоичном
представлении инструкция DX POP имеет вид 01011010, где младшие разряды 010 указывают на
регистр DX. Инструкция POP может быть сформирована путем добавления номера регистра к зна¬
чению 01011000 или в шестнадцатеричном представлении к 58. (Если вы вспомните восьмеричное
счисление, вы сможете понять, что регистры и инструкции могут быть очень удобно представлены в
восьмеричном виде. DX POP будет соответствовать 132, где 13 указывает на POP, а 2 — на регистр
DX). PUSH будет формироваться путем добавления номера регистра к 01010000 или 50 в шестнад¬
цатеричном представлении. XCHG будет соответствовать сумме номера регистра и 10010000 или 90
в шестнадцатеричном виде. Номера регистров можно определить через константы. Так, для микро¬
процессора 8088
0 CONSTANT АХ 4 CONSTANT SP
1 CONSTANT CX ’ 5 CONSTANT BP
2 CONSTANT DX 6 CONSTANT SI
3 CONSTANT BX 7 CONSTANT DI
Теперь может быть сконструировано слово-описатель для одноаргументных мнемокодов:
1ARGMAKE CREATE С, DOES> C@ + С, ,
а мнемоника формируется в другой таблице:
58 1ARGMAKE POP 50 1ARGMAKE PUSH
90 1ARGMAKE XCHG 40 1ARGMAKE INC
и т.д. Теперь, поскольку действие этих производных слов заключается в добавлении числа из стека
к их базовому значению и поскольку номер регистра можно занести в стек с помощью констант,
которые мы описали, вы можете использовать конструкции типа SI PUSH, чтобы добавить 6 к 50 и
229
занести результирующее число 56, соответствующее машинной инструкции, в описании слова типа
CODE.
Использование мнемоники, описанной только таким способом, даст очень ограниченный, хотя и
применимый, поднабор инструкций 8088. Для получения более полного набора команд ассемблера
вы должны позволить мнемонике иметь переменное число аргументов, генерировать две и более ма¬
шинные команды, устанавливать определенные разряды в нужное состояние, чтобы сообщить, на¬
пример, что содержимое регистра следует рассматривать как байт или как пару байтов. Наща зада¬
ча не в том, чтобы предложить вам программу ассемблера для 8088, а в том чтобы изложить, как
это можно сделать. Если у вас есть ассемблер для вашей ЭВМ, вы можете для лучшего понимания
просмотреть его исходные тексты.
Микропроцессоры имеют много разных условных инструкций, работа которых зависит от значе¬
ний флагов. Флаги — это двоичные разряды регистров, обычно называемых регистрами состоя¬
ния. Значения разрядов в регистре состояния определяется различными действиями. Например,
флаг может быть установлен э результате арифметического переполнения, при сравнении содержи¬
мого двух регистров (например, с помощью мнемокода CMP для 8088), если результат вычитания
равен 0, или в результате каких-то других операций. Например, операция
АХ DX CMP
установит флаг Z (нуль) в единичное состояние, если значения содержимого регистров АХ и DX
равны. В MMSFORTH для 8088 команда
4000 " Z JMPC
передаст управление по адресу 4000, если значение флага Z не равно 0, т. e. если содержимое двух
регистров идентично. Мнемоника, используемая для целей ветвления в разных Форт-ассемблерах,
варьируется от версии к версии. В этом случае
~ Z JMPC
эквивалентно стандартной мнемонике 8088 JNZ. ~ соответствует оператору ”NOT”, а Z — это опе¬
ратор, который говорит:”обратите внимание на флаг Z”. To есть если значение флага Z не равно 0,
выполните ветвление. Существует много способов реализации в Форте операций с флагами, вам
следует заглянуть в документацию для вашей версии.
Большинство Форт-ассемблеров поддерживают условные переходы и циклы со структурой, почти
идентичной словам IF...ELSE...THEN, BEGIN...UNTIL и BEGIN...WHILE...REPEAT в словаре
FORTH. Фактически имена слов в контекстном словаре ассемблера могут быть теми же самыми
(как в примерах MMSFORTH, приведенных ниже); возможность использовать слова с идентичными
именами и со сходными, но разными функциями, является главной причиной введения р&зличных
контекстных словарей. Подобно своим эквивалентам в словаре FORTH, между словами условных
переходов и циклов в ассемблере также помещаются слова, но уже имеющие мнемонику ассембле¬
ра. В ассемблере такие слова, как BEGIN...UNTIL вызывают передачи управления в процессе ис¬
полнения команд в рамках отдельного слова. Но в отличие их от эквивалентов в словаре FORTH
передачи управления происходят не на базе кодов, содержащихся в стеке, а на основе значений
флагов. Управляющий флаг должен быть указан в качестве аргумента перед соответствующим сло¬
вом. Рассмотрим пример слова, которое складывает два числа из стека, если они равны, в против¬
ном случае — вычитает. Вы знакомы со всеми этими мнемокодами из предшествующих примеров.
C0DE = IF + ELSE - ( n1 n2 — n3)
BX P0P АХ P0P ( Извлекаем числа из стека)
BX АХ CMP ( Сравниваем их)
Z IF ( Если Z равен 1, т. e. АХ = BX...)
BX АХ ADD ( Складываем числа)
ELSE ( В противном случае ...)
BX АХ SUB ( Вычитаем числа)
THEN ( И в любом случае ...)
АХ PUSH ( Заносим результат в стек)
NEXT END-C0DE
Важным моментом здесь является то, что IF выполняет свою работу, основываясь на значении
когда в регистре состояния Z, заданном оператором Z. Эквивалентная функция была бы выполнена
’’стандартным” Форт-описанием:
=IF+ELSE- ( n1 n2 — n3) 2DUP = IF + ELSE - THEN ;
Хотя последнее описание намного короче и легче читается, в MMSFORTH на IBM PC при исполне¬
нии 100 000 раз оно требует 19 с, в то время как ассемблерная реализация занимает только 5 с. Ес¬
ли быстродействие важно, имеет смысл описать критические по времени слова, используя ассемб¬
230
лер.
Вот пример ассемблерного цикла BEGIN...UNTIL. Здесь перемножаются два числа в стеке путем
последовательного их сложения.
CODE *NEW ( n1 n2 — n3)
0 tt BX MOV ( Обнуление BX, BX - счетчик)
0 tt CX MOV ( Обнуление CX, CX - аккумулятор)
АХ POP DX POP ( Извлекаем числа из стека)
BEGIN ( Начало бесконечного цикла)
BX INC ( Добавляем 1 к BX)
DX CX ADD ( Сложение DX и CX)
АХ BX CMP ( Установка Z * 1 при равенстве)
Z UNTIL ( Продолжение цикла, пока не будет АХ = BX)
CX PUSH (Занесение результата в стек)
NEXT END-CODE
Теперь вы понимаете, как работает *NEW. Это не самый быстрый способ умножения чисел, дейст¬
вие его на 10% медленнее, чем применения слова *, но это хороший пример использования циклов
в ассемблере. Вы узнаете больше о применении циклов и передач управления из упражнений.
Упражнения
1. Микропроцессор Z-80, подобно 8088, характеризует номера 16-разрядных регистров З-разрядными кодами, которые
обычно представляются восьмеричным числом в середине кода команды. Коды регистров двойной длины: ВС “ 0, DE - 2, HL
- 4 и AF-6. Таким образом, DE POP будет соответствовать коду 11010001 или 321 в восьмеричном виде, в то время как HL
POP даст 11100001 или 341 в восьмеричной форме. Опишите константы для номеров регистров. Теперь опишите слово, ана¬
логичное lARGMAKE, которое может быть использовано для описания POP и PUSH. Производные слова с кодом регистра u
качестве аргумента должны компилировать правильные машинные коды. Вам нужно умножить величины констант на неко¬
торое число, прежде чем добавить их к "базовой” величине мнемокода.
2. Опишите слово типа CODE с именем IF-DROP, которое удаляет два числа из стека, если они равны между собой.
3. Опишите 2* на ассемблере (не используйпм циклов).
4. Опишите 10* на ассемблере (используйпм циклы).
Обращение к другим программам, написанным в машинных кодах
Очень часто возникает желание обратиться к подпрограмме, написанной в машинных кодах, из
описаний типа CODE. Такими подпрограммами могут быть программы, загруженные вами в память
с помощью ассемблера, или это могут быть программы, хранящиеся в ПЗУ, или драйверы печа¬
ти/дисплея, загруженные в память другими программами. По сравнению с известными языками об¬
ращение к машинной подпрограмме в Форте весьма простое.
Большинство микропроцессоров и Форт-ассемблеров имеют команду CALL для передачи управ¬
ления по указанному адресу. RET — команда, которая возвращает управление из подпрограммы по
адресу, следующему сразу после команды обращения. Адрес, куда должен быть осуществлен воз¬
врат, обычно укладывается в стек командой CALL и извлекается оттуда командой RET (так работа¬
ют микропроцессоры 8088 и Z-80). Предположим, что мы хотим обратиться к помеченной подпрог¬
рамме из описания CODE. В частности, мы хотим, чтобы подпрограмма извлекла два кода из стека
(до CALL) и занесла их в регистры DX и АХ (в 8088). Ниже представлена реализация этой про¬
граммы:
CREATE P9PEM ASSEMBLER
CX P0P ( Занести в CX из стека адрес возврата,
занесенный туда командой CALL)
DX P0P АХ P0P ( Извлечь из стека нужные значения)
CX PUSH ( Вернуть в стек адрес возврата)
RET ( Вернуться к программе, откуда произошел вызов CALL)
Теперь можно обратиться к POPEM
C0DE MYSWAP ( n1 n2 — n2 n1) P0PEM CALL
DX PUSH АХ PUSH NEXT END-C0DE
или использовать POPEM в ряде других описаний:
C0DE 2DR0P ( n1 n2 —) P0PEM CALL NEXT END-C0DE
23l
CODE 2DUP ( n1 n2 — n1 n2 n1 n2) POPEM CALL
AX PUSH DX PUSH AX PUSH DX PUSH NEXT END-CODE
CODE OVER ( a1 n2 — n1 n2 n1) POPEM CALL
AX PUSH DX PUSH AX PUSH NEXT END-CODE
Это, однако, плохой пример. Хотя мы обратились к подпрограмме в нескольких местах, что стоило
времени и не сэкономило достаточно памяти. Инструкции CALL и RET, а также команды для запо¬
минания и извлечения адресов возврата требуют времени и машинных команд. Чтобы сделать на¬
писание подпрограммы на ассемблере привлекательным, подпрограмма должна быть длиннее той
надстройки, которая необходима для ее вызова, и даже если используется меньше памяти, какое-то
время будет потеряно. Здесь обычно находится компромисс между временем исполнения и эконо¬
мией памяти.
MMSFORTH имеет слово, которое вы, возможно, захотите описать в вашей системе (оно часть сло¬
варя FORTH, а не ASSEMBLER).
• LABEL CREATE [COMPILE] ASSEMBLER ,
LABEL формирует заголовок для подпрограммы в точности так, как это делал оператор POPEM из
нашего примера. Таким образом, можно описать POPEM как
LABEL POPEM CX POP DX POP АХ POP CX PUSH RET
Удобно, не так ли? Слово LABEL позволяет описывать подпрограммы, написанные на ассемблере;
оно позволяет использовать одно и то же слово во многих макроассемблерах.
Вот немного более эффективное применение подпрограммы. Имя ** часто используется для слов,
обозначающих возведение числа в степень. To есть
5 4 **
возведет число 5 в четвертую степень, выдав 625. Это может быть сделано путем умножения второ¬
го сверху числа, хранящегося в стеке, на само себя. Число таких умножений определяется числом
на вершине стека. Мы можем описать ** так, чтобы нужное число раз вызывалась подпрограмма
MULTI. Сначала мы опишем слово, осуществляющее вызов, а затем подпрограмму, хотя на прак¬
тике подпрограмма пишется сначала.
CODE ** ( n1 n2
1 tt BX MOV
АХ POP
DX POP
DX CX MOV
BEGIN-
АХ BX CMP
~ Z WHILE
BX INC
MULTI CALL
REPEAT
CX PUSH
— n3)
Заносим 1 в регистр-счетчик BX)
Заносим показатель степени в АХ)
Заносим число в регистр DX)
Вводим число врегистр произведения)
Запускаем бесконечный цикл)
Выполнено нужное число циклов?)
Если нет...)
Даем приращение содержимому счетчика)
Умножаем произведение на число)
Продолжаем цикл, покане будет выполнено условие)
Заносим результат в стек)
NEXT END-CODE
BX — регистр-счетчик. Когда в результате приращений его значение достигнет величины показате¬
ля степени, работа завершается. CX — регистр произведения, содержит результат MULTI и в ис¬
ходном состоянии должен быть сделан равным числу-аругументу. Обращение к MULTI производит¬
ся столько раз, сколько нужно в рамках цикла BEGIN...WHILE...REPEAT, при этом каждый раз
результат выдается в регистр произведения. MULTI можно описать как
LABEL MULTI
АХ PUSH.BX PUSH DX PUSH
0 tt BX MOV
DX АХ MOV
CX DX MOV
0 tt CX MOV
BEGIN
BX INC
DX CX ADD
АХ BX CMP
Z UNTIL
DX POP BX POP АХ POP
Сохраняем регистры в стеке)
Сбрасываем счетчик в 0)
АХ используется для сравнения показателя со счетчиком)
DX используется для сложения с регистром произведения)
Сброс регистра произведения в 0)
Запуск бесконечного цикла)
Даем приращение счетчику MULTI)
Складываем DX и CX)
Сравниваем АХ и BX)
Пока АХ = BX)
Восстановление регистров)
232
RET ( Возврат к вызвавшей программе)
END-CODE
Вы должны понять, как работает MULTI, путем сравнения с *NEW, описанным ранее. Но есть
одно важное различие. Мы вынуждены были записать содержимое регистров АХ, BX и DX в стек,
поскольку они были нужны в **, а также в MULTI. Содержимое регистров было затем восстановле¬
но из стека до RET в MULTI. Обычно регистры необходимы для различных целей в подпрограмме,
они же используются и в основной программе, одним из выходов из положения является сохране¬
ние их в стеке. Фактически в большинстве языков и в программировании на ’’нормальном” ассемб¬
лере главная функция стека — запоминание величин на время выполнения каких-то операций.
Можно также описать машинные программы без заголовка и затем несколько раз к ним обра¬
щаться при описании других слов. Вот пример описания слов SWAP, 2DUP, OVER и 2DROP с под¬
программами без заголовков и с использованием стека возвратов для передачи адреса подпрограммы
при компиляции:
HERE >R ( Засылка адреса подпрограммы в стек возвратов)
ASSEMBLER ( Делаем ассемблер контекстным словарем)
CX POP OX POP АХ POP CX PUSH RET ( Текст подпрограммы, эквивалентной POPEM)
CODE SWAP R@ CALL DX PUSH АХ PUSH NEXT END-CODE
CODE 2DUP R@ CALL АХ PUSH DX PUSH АХ PUSH DX PUSH NEXT' END-CODE
CODE OVER R@ CALL АХ PUSH DX PUSH DX PUSH NEXT END-CODE
CODE 2DR0P R> CALL NEXT END-CODE
Слово HERE выдает адрес, где начинается подпрограмма, он кладется в стек возвратов, откуда
этот адрес может многократно извлекаться. Каждое описание типа CODE использует адрес из стека
возвратов в качестве аргумента для CALL. Адрес удаляется из стека возвратов командой R> в опи¬
сании 2DROP, после которого он более не нужен. Единственным преимуществом беззаголовочных
подпрограмм по сравнению с LABEL (кроме экономии нескольких байтов) является то, что эти
подпрограммы не могут быть использованы неправильно, так как не могут быть найдены в словаре.
Недостаток такого метода заключается в том, что это делает программу трудночитаемой. Вы долж¬
ны также помнить, что применение подпрограмм усложняет отладку программ на ассемблере.
Конечно, не важно, если вы не можете обратиться к подпрограмме, которую не вы создали. Если
же вы используете Форт в рамках операционной системы, в вашем распоряжении много подпрог¬
рамм, таких как драйверы печатающего устройства или интерфейса RS-232, графические програм¬
мы, процедуры для работы с диском и т.д., к которым можно обратиться из слов типа CODE. Если
ваша ЭВМ имеет версию Бейсика в ПЗУ, она включает в себя подпрограммы арифметики с плава¬
ющей точкой. Вы можете воспользоваться ими, если выяснить, как к ним обращаться и что они де¬
лают со стеком и регистрами. Существует много книг, которые описывают резидентные программы
в наиболее популярных микроЭВМ. Однако необходима осторожность, так как вы можете не знать
все, что делает данная подпрограмма. Вам нужно убедиться, что любые регистры, которые содержат
информацию, полезную для Форта, и которые будут использованы в подпрограмме (указатели, на¬
пример), сохраняются в стеке и восстанавливаются после обращения к программе. Вы должны
знать, что делает программа со стеком и указателем стека, так как вы не хотите терять информа¬
цию при обращении. Вы должны также быть уверены, что подпрограмма не пытается занести что-
то в область памяти, занятую словарем. Вы возможно, сможете в результате расследования и опре¬
деленных проб и ошибок выяснить некоторые вещи, но этот процесс потребует вашего терпения.
Если у вас есть дисассемблер, — это упростит задачу. (Дисассемблер просматривает программу в
машинных кодах и транслирует ее в мнемоническую форму, облегчая понимание программы.)
Программы в машинных кодах могут быть получены из других источников, таких как статьи в
журнале и подпрограммы, встроенные в Бейсик-программы. Если у вас есть исходный текст такой
программы на ассемблере, обычно проще заставить ее работать в Форте, используя Форт-ассемблер.
Если это не практично, вы можете скомпилировать машинную программу в слово, описанное с по¬
мощью LABEL, используя C,. Ьы должны быть уверены, однако, что программа перемещаема, т.е.
она не содержит каких-либо абсолютных адресов передач управления или вызовов, поскольку адрес
передачи управления почти наверняка изменится после того, как вы встроете программу в слово.
Все передачи управления и вызовы должны быть относительными, т.е. заданными величиной сме¬
щения к адресу позиции, откуда производится вызов.
Но существуют исключения. Некоторые версии Форта позволяют перемещать верхнюю часть
словаря Форта из области больших адресов в начальную часть памяти (смотри карту памяти в гл.
14), освобождая место для большого числа машинных программ, которые созданы для работы в вер¬
хней части памяти. Если это так, вы можете поместить машинную программу в массив и переме¬
233
щать ее с помощью CMOVE в верхнюю область памяти, используя адрес обращения, предусмотрен¬
ный в оригинальной версии программы. Мы использовали таким способом на TRS-80 очень слож¬
ный драйвер координатографа. Это снова потребует экспериментов.
Упражнения
1. Часто полезно сохранить регистры в стеке на время выполнения машинной программы, так что вы сможете просмот¬
реть их содержимое при отладке программы после того, как она выполнена. Используйте LABEL для описания
SAVESTACK, чтобы заносить в стек содержимое регистров AX, BX, CX и DX 8088. Исполнение SAVESTACK CALL fe про¬
грамме будет выполнять тахой перенос. Будьте осторожны, не допускайте путаницу с адресом возврата!
2. В верхней части памяти вашей ЭВМ с адресом FF00 есть программа для управления координатографом. Она рисует
арифметические символы и требует, чтобы в регистр CX было занесено значение высоты символа в сотых долях дюйма, а в
регистр DX — ASCII-код символа. Опишите слова с именем PLOTEMIT, для которого высота символа должна лежать во
второй сверху позиции стека, а ASCII-код — на вершине и которое обращается к драйверу координатографа.
3. У вас имеется 16-канальный аналого-цифровой преобразователь, связанный с вашей микроЭВМ. Он опрашивается ма¬
шинной программой по адресу FF00. Номер канала может быть передан в программу через регистр AX, и программа возвра¬
щает результат для данного канала в милливольтах через тот же регистр. Опишите слово GETDATA,
которое воспринимает номер канала из стека и туда же кладет результат в милливольтах. To есть если в канале 8 напря¬
жение равно 528 мВ,
8 GETDATA
запишет в стек число 528.
4. Программа из упражнения 3 изменяет величины в регистрах SI и BX, и вы должны быть уверены, что они не измени¬
лись при завершении исполнения GETDATA. Чтобы решить эту проблему, переопределите GETDATA.
Выводы
Форт — быстродействующий язык, даже если не использовать ассемблер. А ассемблер труднее применить, чем Форт, и
он работает только на одном типе процессора. Главная проблема использования ассемблера в Форте заключается не в том,
чтобы вставить все, что можно, в CODE-слова, а в том, чтобы применять разумную мнемонику. Когда эффективен ассемб¬
лер? При выполнении двух условий: если вы хотите, чтобы программа работала быстрее, или когда вы обнаружили что-то,
что вы не можете сделать в Форте (последнее случается редко). В обоих случаях вы захотите использовать ассемблер в ми¬
нимальном объеме. Вы добьетесь этого, правильно факторизируя Форт-программу. Те небольшие части, которые использу¬
ются большую часть времени, так как они исполняются crfoea и снова, могут быть оформлены в виде коротких CODE-слов.
На ассемблере могут быть запрограммированы такие критические функции, как обслуживание аналого-цифрового преобра¬
зователя или координатографа, которые не поддерживаются Фортом. Каждое слово Форта должно быть коротким и про¬
стым, это правило относится и к CODE-словам.
Цикл — редактор-ассемблер-редактор-связей-загрузка-исполнение-отладка, который необходим для любого ассемблера,
исключается в Форте. Форт-ассемблер позволяет компилировать и тестировать машинную программу небольшими частями,
так же как и обычную Форт-программу. В результате программирование оказывается более производительным, а программы
— лучше организованными и более эффективными. Время разработки программы в Форте составляет лишь долю того, что
требуется для случая чисто ассемблера.
Мы начали эту книгу, сказав, что Форт позволит нам применить все доступные особенности вашей ЭВМ, использовать все
быстродействие, на которое она способна, и сделать это более легко, чем в любом другом языке. Разумно закончить книгу
описанием Форт-ассемблера, поскольку именно комбинация применения ассемблера и ’*нормального” Форта обеспечивает
полную эффективность. Форт может быть использован на разных уровнях: простейший — интерактивный калькулятор для
коротких программ без долговременного их хранения, наиболее сложный уровень — применение Форт-слов высокого уропня
в комбинации с ассемблером для создания комплексных программ, таких как контроллеры процессов в реальном масштабе
времени, системы управления базами данных, экспертные системы и даже другие языки программирования.
ПРИЛОЖЕНИЕ А
ГЛОССАРИЙ
Список слов Форта
Этот глоссарий включает слова Форт-79 и Форт-83, некоторые нестандартные слова из
MMSFORTH и прочих версий и другие полезные слова, которые определены в тексте и упражнени¬
ях. При описании слов используются следующие сокращения:
I слова немедленного исполнения
С может использоваться только в режиме
компиляции
E может использоваться только в режиме
исполнения
83REQ слова Форт-83 из обязательного списка
83ASM ассемблер Форт-83
83DBL слова Форт-83 для работы с числами двойной
длины
83SYS системные слова Форт-83
83CNT слова Форт-83 из контролируемого списка
83UNC слова Форт-83 из неконтролируемого списка
83FLD экспериментальные слова Форт-83 для пре
образования адресов полей
83SRC экспериментальные слова Форт-83 для спе
цификации порядка поиска и управления
79REQ слова Форт-79 из обязательного списка
79ASM слова ассемблера Форт-79
79DBL слова Форт-79 для работы с числами двойной
длины
79RSV слова Форт-79 из резервного списка
MMS слова MMSFORTH
FIG слова FIGFORTH
MVP слова MVPFORTH
F83 слова версии Форта Лаксена и Перри
VAR слова, встречающиеся в некоторых версиях
TXT слова, описанные в тексте книги
Определения в этом глоссарии взяты из стандартов Форт-83 и Форт-79, но большинство из них
переписаны для того, чтобы сделать их более понятными и подчеркнуть отличия стандартов. Этот
глоссарий не заменяет описаний стандартов и не является исчерпывающим руководством, включаю¬
щим в себя описания всех нестандартных слов (из контролируемого и неконтролируемого списков, а
также резервных слов), которые встречаются в стандартах. Из MMSFORTH включены только те
слова, которые встречаются в тексте. MMSFORTH содержит в себе все слова Форт-79 и многие сло¬
ва помимо этого. Аналогично включены только те слова из других версий, которые встречаются в
книге. Форт-описания, которые приведены для слов, имеющихся в MMSFORTH, не являются собст¬
венно MMSFORTH-описаниями. В некоторых случаях даются ссылки на более полные описания в
тексте. Конечно, в тех случаях, где нет ссылок, следует воспользоваться предметным указателем.
Элементы глоссария упорядочены в соответствии с ASCII-кодами.
! ’’store” Сприсвоить") 83REQ 79REQ
( n адр -)
Записывает n no адресу ”адр”.
# ’’sharp” 83REQ 79REQ
( dl - d2)
235
Самая правая цифра dl преобразуется в ASCII-символ в соответствии со значением BASE и за¬
носится в форматированную выходную строку (для последующего вывода с помощью TYPE).
d2—число, содержащее оставшиеся цифры, используемые для последующей переработки. Использу¬
ется между <# и #>. Слово для отображения числа центров, представленного числом двойной дли¬
ны со знаком, в виде числа долларов и центов может быть описано как
: .DOLLARS ( d - )
SWAP OVER DABS <# # # 46 HOLD #S 36 HOLD ROT SIGN #> TYPE ;
Например: 5236 .DOLLARS отобразит $52.36. См. также <#, #, #S, HOLD, SIGN, #>.
#> ’’sharp-greater” 83REQ 79REQ
( d — адр n)
Завершает преобразование числа в форматированную выходную строку, ”адр”— адрес выходной
строки-результата, а n — число символов в ней, ”адр п” удобно использовать совместно с TYPE.
Для примера смотри #. См. также #, <#, #S, HOLD, SIGN.
#IN "number-IN” "число — IN" MMS
( - n)
Выдает запрос ”?” и производит ввод числа. Младшие 16 битов заносятся в стек, а старшие 16
битов записываются в HI#. Ввод нецифровых символов вызовет отклик ”? Redo”, предо-ставляя
возможность повторить ввод.
#PT ’’number-p-t” ”число-p-t” MMS
( - адр)
Слово QUAN, определяет положение запятой в числе, введенном последним. Если запятой нет,
результат равен 0. Таким образом,
1234. #PT
занесет в стек 1.
#S "sharp-s” 83REQ 79REQ
( d -0 0)
Преобразует значение d цифра за цифрой в форматированную выходную строку в соответствии с
величиной BASE. См., например, #. В выходную строку будет занесен один нуль, если d было с са¬
мого начала равно 0. Используется между <# и #>.
#TIB ”number-t-i-b” ”число t-i-b" 83REQ VAR
( -- адр)
Переменная, где хранится число байтов, лежащих в данный момент во входном текстовом буфе¬
ре. Значение, которое заносится в #TIB, может лежать в диапазоне от 0 до максимального числа
символов, помещающихся во входном текстовом буфере (стандарты требуют минимум 80 симво¬
лов).
$! ’’string-store” "записать строку" MMS
( адр1 адр2 ~)
Переносит содержимое счетной строки (включая байт-счетчик) из адреса ”адр1” в ”адр2”.
$” ’’string-quote” "ввести строку" MMS
( — адр)
Запоминает строку, ограниченную ” (двойной кавычкой) в PAD, адрес которого заносится в стек в
качестве ’адр”. Строка записывается в счетном формате. Таким образом;
$” Foxy” COUNT TYPE
отобразит на экране ”Foxy”.
$+ "string-concatenate” "соединить строки" MMS
( адр1 адр2 - адрЗ)
Добавляет счетную строку, лежащую по адресу ”адр2” (без байта-счетчика), к правому концу счет¬
ной строки по адресу ”адр1” и помещает счетную строку-результат в буфер PAD, адрес которой в
виде ”адрЗ” заносится в стек.
$-TB ”string-minus-t-b” "строка-минус-tb" MMS
( $адр — $адр)
Удаляет пробелы (ASCII 32) в конце счетной строки путем уменьшения байта-счетчика на их чис¬
ло. Сравните с -TRAILING.
$. ”string-dot” "строка-точка" MMS
( адр ~)
Отображает счетную строку, лежащую по адресу ’’адр”. Эквивалентно COUNT TYPE.
$ARRAY ’’string-array” "массив строк" MMS
( nl n2 ~)
236
Слово-описатель, которое создает массив строк. При использовании в форме
nl n2 $ARRAY <name>
создает статью в словаре с именем <name> и резервирует место для n2+l счетных строк с макси¬
мальной длиной nl+l байтов каждая. Когда к <name> производится обращение:
n <name>
в стек заносится адрес строки-элемента с номером n+l.
$CHAR ”string-char” ”строка-символ” TXT
( n — адр)
Создает счетную строку из одного символа, соответствующего ASCII коду (n), который лежит в сте¬
ке и записывает ее в PAD, адрес которого заносится в стек в виде ”адр”. См. также CHR$.
: $CHAR 1 PAD С! PAD 1+ С! PAD ;
$COMPADE ’’string-compare” "сравнение строк” MMS
( адр1 адр2 — флаг)
Сравнивает две счетные строки, занося в стек -1, 0 или 1, в зависимости от того, является ли стро¬
ка с адресом ”адр1” меньше чем, равна или больше чем строка с адресом ”адр2”. Сравнение произ¬
водится по схеме символ-за-символом. Слово полезно в программах сортировки строк.
$CONSTANT ’’string-constant” "строка-константа” MMS TXT
( --)
Слово-описатель, которое создает строку-константу. При использовании в форме
$CONSTANT <name> string”
формирует статью словаря с именем <name> и компилирует последующую строку (вплоть до разде¬
лителя ”) в счетном формате. При исполнении <name> в стек заносится адрес счетной строки.
$GET ”string-get” "принять строку" TXT
( адр ~)
Записывает строку по адресу ”адр”. При использовании в форме:
$GET <string>"
строка <string> (вплоть до разграничителя ”) записывается в счетном формате по адресу ”адр”.
: $GET 34 WORD DUP C@ 1+ ROT SWAP CMOVE ;
$IN ”string-in” "ввести строку" TXT
( - адр)
Выдает на экран запрос ”?” и ждет ввода с клавиатуры до 255 символов. Когда будет введено 255
символов или поступит код ’’возврата каретки”, введенная последовательность будет записана в ви¬
де счетной строки в PAD, адрес которого будет занесен в стек как ”адр”.
: $IN PAD 1+ 255 2DUP BLANK ." ? " 2DUP EXPECT
-TRAILING PAD С! 1- ;
См. также IN$.
$SWAP ”string-swap” "поменять строки местами" TXT
( адр1 адр2 ~)
Меняет местами счетные строки с адресами ”адр1” и ”адр2”. Строки должны иметь равную макси¬
мальную зарезервированную длину.
: $SWAP DUP DUP C@ 1+ >R PAD SWAP R@ CMOVE
SWAP DUP ROT R@ CMOVE
PAD SWAP R> CMOVE ;
См. также $XCHG.
$VARIABLE ’’string-variable” "строка-тременная" MMS
( n ~)
Слово-описатель, которое создает строки-переменные. При использовании в форме
n VARIABLE <name>
формирует статью в словаре с именем <name> и резервирует n+l байтов для запоминания счетной
строки (в исходный момент байт-счетчик равен 0). При исполнении,<пате> в стек заносится адрес,
где лежит эта строка.
$XCHG ’’string-exchange” "обмен строками" MMS
( адр1 адр2 «)
Мецяет местами счетные строки по адресам ”адр1” и ”адр2”. Строки должны иметь идентичные
максимальные длины. См. также $SWAP.
* ”tick” "апостроф” I (79) 83REQ 79REQ
Определения 4 (апостроф) в Форт-79 и Форт-83 отличаются существенно. В Форт-83 при использо¬
вании
237
4 <имя>
в стек заносится адрес поля программы слова <имя>. В Форт-83 4 не является словом немедленного
действия и обычно используется в режиме исполнения. Противостоит [4]. В Форт-79 4 выполняет
одну из двух операций в зависимости от того, в режиме компиляции или исполнения находится си¬
стема. В режиме исполнения
* <имя>
засылает в стек адрес поля параметров слова <имя>, в то время как в режиме компиляции адрес
поля параметров компилируется в качестве литерала, который при последующем исполнении ском¬
пилированного слова помещается в стек. В Форт-79 ’ является словом немедленного исполнения. В
обоих стандартах при отсутствии <имя> в словаре дается сообщение об ошибке. В Форт-83 команда
4 <имя> >BODY
засылает тот же адрес поля параметров, что и
4<имя>
в Форт-79.
("рагеп” "скобка” I 83REQ 79REQ
Выделяет комментарии, которые должны быть проигнорированы в исходном тексте программы.
Форма использования
( ккк)
Символы ”ккк”, выделенные справа ) (закрывающая скобка), считаются комментарием и не обра¬
батываются. Пробел необходим за (, но не должен предшествовать символу ), который рассматрива¬
ется в качестве разделителя, а не слова Форта. ( можно использовать как в режиме исполнения,
так и компиляции. Число символов в ”ккк” может лежать в диапазоне от 0 до числа символов, ос¬
тавшихся во входном потоке вплоть до закрывающей скобки. В Форт-79, если входной поток иссяк¬
нет до закрывающей скобки, будет дано сообщение об ошибке. См. также \.
* ’’times” ”умножить" 83REQ 79REQ
( nl n2 — n3)
Умножает nl на n2, выдавая произведение n3. n3 будет содержать младшие 16 битов произведения,
даже если происходит арифметическое переполнение.
** ’’power” "возвести в спмпень" 83UNC 79RES TXT
( nl n2 -- n3)
Возводит nl в степень n2, результат n3 кладет в стек.
: ** ?DUP 0-
IF DROP 1
ELSE DUP 1 - IF DROP
ELSE OVER SWAP 1- 0
DO OVER * LOOP SWAP DROP
THEN
THEN ;
*/ ’’times-divide” "умножить-раздёлить" 83REQ 79REQ
( nl n2 n3 — n4)
Умножает nl на n2, результат делит на n3, засылает в стек частное n4. Произведение nl на n2
представляется в виде промежуточного результата двойной длины, обеспечивая большую точность,
чем при традиционной последовательности
nl n2 * n3 /
В Форт-83 используется деление с нижней границей1, в то время как в Форт-79 частное округляет¬
ся в направлении нуля.
*/MOD ”times-divide-mod” "умножить-разделитьсостатком"ЪЪКЕ§ 79REQ
( nl n2 n3 -- n4 n5)
Умножает nl на n2, результат делит на n3, остаток n4 и частное n5 засылаются в стек. Как и в
случае */, промежуточное произведение имеет двойную длину. В Форт-83 применено деление с
нижней границей.
+ ”plus” "плюс" 83REQ 79REQ
( nl n2 -- n3)
1 Частное округляется в строну нижней границы (floored), а остаток имеет знак делителя. Деление -8/5 дает частное -2 в
Форт-83 и -1 в Форт-79.— Прим.перев.
238
Добавляет nl к n2 и выдает сумму n3. n3 содержит младшие 16 битов суммы даже в случае ариф¬
метического переполнения.
+! ”plus-store” ”плюс-присвоить" 83REQ 79REQ
( nl адр —)
Добавляет nl к числу одинарной длины, хранящемуся по адресу ”адр”, замещая старое значение
полученным результатом.
+LOOP "plus-loop” "плюс-цикл” I, С 83REQ 79REQ
( n ~)
Завершает do-loop, позволяя увеличивать (или уменьшать) индекс цикла на величину, большую
чем 1. При использовании в форме
: <name> ... DO ... +LOOP ;
компилирует структуру do-loop. Когда <name> исполняется, +LOOP предполагает наличие числа (n)
в стеке, которое определяет величину приращения индекса в цикле. В Форт-83 ”п” добавляется к
индексу цикла, и если сумма ’’пересечет” границу между значением предела цикла минус единица
и самим пределом, то цикл завершается. В Форт-79 цикл завершается, когда индекс становится
больше или равен пределу (с учетом знака). В обоих стандартах управление передается слову, сле¬
дующему за соответствующим DO, если цикл не завершился, и слову после +*LOOP, — если завер¬
шился. Противостоит LOOP. См. обсуждение завершения цикла в гл. 8. См. также DO.
, ’’comma” "запятая” 83REQ 79REQ
( n ~)
Записывает ”п” в очередную свободную ячейку словаря (адрес которой определяется словом HERE)
и увеличивает указатель словаря на 2, чтобы зарезервировать место для ”п”. При этом говорится,
что ”п” скомпилировано по адресу HERE. Например, если XYZ было определено как
CREATE XYZ 153 ,
то команда
XYZ @ .
отобразит скомпилированное число 153. См. также C,.
- "minus” "минус” 83REQ 79REQ
( nl n2 — n3)
Вычитает n2 из nl, остаток n3 кладет в стек.
~> ”next-block” ”следуюищй блок" I 83CNT 79REQ TXT MMS
( ~)
Немедленно переключают интерпретацию на начало следующего по порядку блока.
: ~> 1 BLK +! ; IMMEDLATE
-ROT”minus-rote” "минус-ROT" TXT MVP VAR
( nl n2 n3 — n3 nl n2)
Засылает верхний код стека в третью его позицию. Эквивалентно ROT ROT.
-TRAILING ”minus-trailing” 83REQ 79REQ
( адр nl — адр n2)
Корректирует число символов nl текстовой строки, начинающейся по адресу ”адр”, чтобы вычис¬
лить число n2, которое не включает пробелы, завершающие строку. ”адр” не изменяется. Если nl
равно 0, n2 также будет равно 0, если в строке содержатся только пробелы, то и n2 будет равно 0.
Часто используется для определения числа введенных символов при работе с EXPECT. Более де¬
тальная информация содержится на cmp 216.
. "точка” 83REQ 79REQ
( n ~)
Отображает в текущей позиции дисплея величину ”п” (включая знак минус, если ”п” отрицатель¬
но) в соответствии со значением BASE. После числа вводится пробел.
.” ”dot-quote” I,C 83REQ 79REQ
( ~)
Компилирует и (или) отображает текст. При использовании в форме
: <name> ... .” ccc" ... ;
компилирует текст ”ссс”, следующий после .” вплоть до разграничителя ” 0*войная кавычка) в
описании слова <name>. За .” должен следовать пробел (и он не включается в последовательность
”ссс”), а перед ” (двойная кавычка), которая является разграничителем, а не словом Форта, пробел
не требуется. Откомпилированный текст будет отображен при исполнении <name>. B Форт-83 слово
.” может использоваться только в режиме компиляции. (В противоположность .( .) В Форт-79 .”
может использоваться в режиме исполнения; в этом случае текст немедленно отображается. Форт-
239
79 предполагает также, что текст может содержать до 127 сймволов или даже более, и, если вход¬
ной поток иссякнет до завершающей ”, будет дано сообщение об ошибке.
.( ”dot-paren” "точка-скобка" I 83REQ
( ~)
При использовании в форме
.( ccc)
немедленно отображает текст, следующий за .( вплоть до разграничителя ) (закрывающая скобка),
как в режиме компиляции, так и исполнения. Пробел необходим после .( (и он не включается в по¬
следовательность ”ссс”), но пробел не должен предшествовать закрывающей скобке, которая явля¬
ется разграничителем, а не словом Форта. Полезно для комментариев, которые отображаются при
интерпретации блоков. Противостоит .”.
.BIN "dot-binary” Vточка двоичная" TXT
( n — n)
Отображает n в двоичном представлении без изменения состояний стека или BASE.
: .BIN DUP BASE @ 2 BASE ! SWAP . BASE ! ;
.BLK "dot-b-1-k” "точка-b-l-k" ’ I TXT
( ~)
Отображает номер интерпретируемого в данный момент блока
(или 0 при режиме исполнения).
: .BLK BLK @ U. ; IMMEDLVTE
.DEC ”dot-decimal” "точка десятичная” TXT
( n - n)
Отображает n в десятичном представлении без изменения указателя стека или BASE.
: .DEC DUP BASE @ DECIMAL SWAP . BASE ! ;
.HEX "dot-hex” "точка-НЕХ” TXT
( n — n)
Отображает n в шестнадцатеричном представлении без изменения указателя стека или BASE.
: .HEX DUP BASE @ 16 BASE ! SWAP . BA$E ! ;
.L ”dot-l” "точка-Г TXT
( nl n2 ~)
Отображает nl в соответствии с величиной BASE в поле шириной n2 позиций так, что старшая
цифра занимает самую левую позицию. См. также .R.
; .L SWAP 0 <# #S #> ROT OVER — ROT ROT TYPE SPACES ;
.LINE ”dot-line” ”точка-строка” TXT
( nl n2 ~)
Отображает строку n2 в блоке nl. n2 может принимать значения от 0 до 15.
: .LINE SWAP BLOCK SWAP 64 * + 64 TYPE ;
.LSB ”dot-l-s-b” "точка-l-s-b" TXT
( n --)
Отображает младший байт числа n.
: .LSB 255 AND . ;
.MEM ”dot-mem” "точка-тет" TXT MMS
( ~)
Отображает число байтов между PAD и верхом стека. В большинстве версий — это объем свободной
памяти.
: .MEM SP@ PAD — U. ;
.MSB ”dot-m-s-b” "точка-т-s-b” TXT
( n --)
Отображает старший байт числа n.
: .MSB 256 / . ;
.NUMS ”dot-numbers” ”точка-числа" TXT
Отображает число п.в двоичном, восьмеричном, десятеричном и шестнадцатеричном представлени¬
ях, не изменяя BASE.
: .NUMS .BIN .OCT .DEC .HEX DROP ;
.OCT ”dot-octal” "точка восьмеричная" TXT
( n — n0)
Отображает число n в восьмеричном представлении, не изменяя указатель стека или величину
BASE.
240
: .OCT DUP BASE @ 8 BASE ! SWAP . BASE ! ;
.R ”dot-r” "точка-г" 83CNT 79RES MMS
( nl n2 ~)
С учетом значения BASE отображает число nl в поле длиной n2 так, что младшая цифра занимает
самую правую позицию поля. Если nl отрицательно, перед ним печатается знак минус. В Форт-83,
если необходимое число символов для отображения nl больше n2, дается сообщение об ошибке.
Вазмер поля менее 1 в Форт-83 также запрещен. В Форт-79 при требуемом числе символов для ото¬
бражения nl больше n2 ошибка не фиксируется. (В большинстве версий Форт-79 печатает в таком
случае результат без предшествующего пробела). Если в Форт-79 n2 меньше 1, предшествующий
пробел также не вводится.
.S ”dot-s” "точка-s" TXT MMS
( ~)
Отображает все числа в стеке без изменения его указателя. В Форт-83 описание имеет вид
: .S DEPTH ?DUP 0-
IF ." Stack empty”
ELSE 0 DO DEPTH 1- ROLL DUP . LOOP
THEN ;
а в Форт-79
: .S DEPTH ?DUP 0-
IF .” Stack empty”
ELSE 0 DO DEPTH ROLL DUP . LOOP
THEN ;
/ ’’divide” "разделить" 83REQ 79REQ
( nl n2 — n3)
Делит nl на n2, частное n3 засылает в стек. В Форт-79 n3 округляется в направлении нуля, в то
время как в Форт-83 используется деление с нижней границей. В Форт-83 при делителе, равном 0,
или если частное оказывается вне пределов -32.768 — 32.767, дается сообщение об ошибке.
/MOD ”divide-mod” "деление с остатком" 83REQ 79REQ
(nl n2 — n3 n4)
Делит nl на n2, остаток n3 и частное n4 засылаются в стек. В Форт-83 применено деление с ниж¬
ней границей, в Форт-79 частное округляется в направлении 0. В Форт-83 при делителе, равном 0,
или если частное оказывается вне пределов -32.768 — 32.767, дается сообщение об ошибке.
0< ”zero-less” "меньше нуля” 83REQ 79REQ
( n — флаг)
Сравнивает n с 0 и засылает в стек флаг истинно, если n меньше 0.
G- ”zero-equals” "равно нулю" 83REQ 79REQ
( n -- флаг)
Сравнивает n с 0 и засылает в стек флаг истинно, если n равно 0. См. также NOT, которое явля¬
ется синонимом 0= в Форт-79, но имеет совсем иной смысл в Форт-83.
0> ”zero-greater” "больше нуля" 83REQ 79REQ
( n — флаг)
Сравнивает n с 0 и засылает в стек флаг истинно, если n больше 0.
OARGMAKE ”0-arg-make” TXT
( n —)
Слово-описатель, которое формирует мнемонику ассемблера, чтобы скомпилировать байт, который
при описании является аргументом. При использовании в форме
n OARGMAKE <name>
формирует статью словаря с именем <name> и компилирует младшие 8 битов числа n в поле пара¬
метров <name>. При исполнении <name> байт в его поле параметров будет скомпилирован в сло¬
варь. (Подробно рассмотрено на стр. 229).
1+ ”one-plus” "прибавить единицу" 83REQ 79REQ
( nl — n2)
Добавляет 1 к nl и кладет результат в стек n2.
1- ’'опе-т'тив""вычестьединицу" 83REQ 79REQ
( nl - n2)
Вычитает 1 из nl и результат n2 кладет в стек.
241
16* "sixteen-times” '*умножить на 16” MMS
( nl - n2)
Умножает nl на 16 и результат n2 кладет в стек.
2! ”two-store” ”два-присвоить” 83DBL 79DBL
( d адр —)
Записывает число двойной длины по адресу ”адр”.
2$ARRAY ”two-string-array” "двумерный массив строк” MMS
( nl n2 n3 --)
Слово-описатель, которое создает двумерный массив строк (матрицу). При использовании в форме
nl n2 n3 2$ARRAY <name>
формирует статью в словаре с именем <name> и резервирует место для счетных строк с максималь¬
ной длиной nl+l при числе рядов n2+l и числе столбцов n3+l. Когда массив <name> используется
как
nl n2 <name>
в стек заносится адрес начала строки, лежащей в ряду nl+l и столбце n2+l.
2* ”two-times” Умножить на 2” 83CNT 79RES MMS
( nl - n2)
Умножает nl на 2, результат n2 заносит в стек.
2+ ”two-plus” "прибавить 2” 83REQ 79REQ
( nl - n2)
Прибавляет 2 к nl, а результат n2 заносит в стек.
2- ”two-minus” ”вычесть 2” 83REQ 79REQ
( nl - n2)
Вычитает 2 из nl, результат n2 заносит в стек.
2/ ”two-divide” ”разделить на 2” 83REQ 79REQ
( nl ~ n2)
Делит nl на 2, результат n2 заносит в стек.
2@ ”two-tetch” Vизвлечь двойное число” 83REQ 79REQ
( адр — d)
Кладет в стек число двойной длины, лежащее по адресу ”адр”.
2ARRAY ”two-array” ”двумерный массив” MMS
( nl n2 --)
Слово-описатель, которое создает двумерный массив (матрицу) чисел одинарной длины. При ис¬
пользовании в форме
nl n2 2ARRAY <name>
формирует статью в словаре с именем <name> и резервирует место для nl+l рядов и n2+l столбцов
чисел одинарной длины. При обращении
nl n2 <name>
в стек заносится адрес элемента, лежащего в ряду nl+l и столбце n2+l. См. также 2CARRAY;
ARRAY.
2CARRAY ”two-c-array” '*2-с-массив” MMS
( nl n2 -)
Слово-описатель, которое создает двумерный массив (матрицу) байтов. При использовании в
форме
nl n2 2CARRAY <name>
формирует статью в словаре с именем <name> и резервирует место для nl+l рядов и n2+l столб¬
цов байтов. При обращении к <name>
nl n2 <name>
в стек заносится адрес элемента, лежащего в ряду nl+l и колонке n2+l. €м. также 2ARRAY;
ARRAY.
2CONSTANT ”two-constant” ”констанпш
двойной длины” 83DBL 79DBL
( d --) или (nl n2 --)
Слово-описатель, которое создает константу двойной длины (она может использоваться также для
записи двух чисел одинарной длины). При использовании в форме
d 2CONSTANT <name> или nl n2 2CONSTANT <name>
242
формирует статью в словаре с именем <name> и компилирует число двойной длины (или два числа
одинарной длины) из стека. Когда <name> исполняется, d (или nl и n2) засылаются в стек. См.
также CONSTANT; CCONSTANT; 4CONSTANT. Противостоит 2VARIABLE.
2DROP "two-drop” ’*2-DROP" 83DBL 79DBL
( d ~)
Удаляет из стека число двойной длины (или два числа одинарной длины).
2DUP ”two-dupe” '*2-DUP" 83DBL 79DBL
( d — d d) или (nl n2 — nl n2 nl n2)
Дублирует в стеке число двойной длины (или пару чисел одинарной длины).
20VER ”two-over” "2-OVER" 83DBL 79DBL
( dl d2 — dl d2 dl) или (nl n2 n3 n4--nl n2 n3 n4 nl n2)
Копирует второе сверху число двойной длины и кладет его на верх стека или копирует третье и
четвертое сверху числа одинарной длины и кладет их на верх стека.
2QUAN ”two-quan” ’*2-QUAN" MMS
( ~)
Слово-описатель, которое создает слова типа QUAN для чисел двойной длины. При использовании
в форме
2QUAN <name>
формируе± в словаре статью с именем <name> и резервирует место для числа двойной длины. Ког¬
да слово <name> используется само, код, который оно содержит, заносится в стек. Если перед ним
стоит IS, число из стека заносится в поле параметров <name>. Если же перед ним стоит АТ, в стек
заносится адрес поля параметров слова <name>. (Более подробно о QUAN написано на стр.82) См.
также QUAN; CQUAN; 4QUAN.
2ROT ”two-rote” '*2-ROT'' 83DBL 79DBL
( dl d2 d3 — d2 d3 dl) или ( nl n2 n3 n4 n5 n6 — n3 n4 n5 n6 nl n2)
Засылает третье сверху число двойной длины в стеке на его верх (или засылает третью сверху пару
чисел на верх стека).
2SWAP ”two-swap” V-SWAP" 83DBL 79DBL
( dl d2 — d2 dl) или ( nl n2 n3 n4 — n3 n4 nl n2)
Меняет местами в стеке два верхних числа двойной длины (или две верхние пары чисел одинарной
длины).
2VARIABLE ”two-variable” ”переменная двойной
длины" 83DBL 79DBL
( -)
Слово-описатель, которое создает переменную двойной длины. При использовании в форме
2VARlABLE <name>
формирует статью в словаре с именем <name> и резервирует 32 бита в поле параметров для хране¬
ния числа двойной длины. Когда <name> исполняется, адрес поля параметров <name>, где хранится
число двойной длины, засылается в стек. 2VARIABLE не обязательно инициализирует значение пе¬
ременной. См. также VARIABLE; CVARIABLE; 4VARIABLE. Противостоит 2CONSTANT.
4! ”two-store” "4-присвоить" MMS
( fl адр --)
Записывает 64-разрядное число из стека в память по адресу ”адр”. Обычно используется для чисел
с плавающей запятой.
4@ ”four-fetch” "4 извлечь" MMS
( адр — fl)
Засылает в стек 64-разрядное число, лежащее по адресу ”адр”. Обычно используется для чисел с
плавающей запятой.
4ARRAY ”four-array” "массив чисел учетверенной
длины" MMS
( fl ~)
Слово-описатель, которое создает линейный массив (вектор) с 64-битовыми элементами (обычно
используемыми для чисел с плавающей запятой). При использовании в форме
n 4ARRAY <name>
формирует статью в словаре с именем <name> и резервирует место для n+l числа с плавающей за¬
пятой. Когда <name> используется в форме
n <name>
243
в стек заносится адрес (n+l)-ro элемента массива. См. также ARRAY; CARRAY. Противоположно
2ARRAY.
4CONSTANT "four-constant” MMS
( fl -)
Слово-описатель, которое создает 64-битовую константу (обычно для чисел с плавающей запятой).
При использовании в форме
fl 4CONSTANT <name>
формирует в словаре статью с именем <name> и компилирует число с плавающей запятой fl из сте¬
ка в поле параметров <name>. Когда <name> исполняется, число с плавающей запятой засылается в
стек.
4QUAN ”four-quan” "4-QUAN" MMS
( -)
Слово-описатель, которое создает слова типа QUAN для 64-битовых чисел. При использовании в
форме:
4QUAN <name>
формирует в словаре статью с именем <name> и резервирует 64 бита для числа с плавающей запя¬
той. Когда слово <name> используется само, код, который оно содержит, заносится в стек. Если пе¬
ред ним стоит IS, значение, хранящееся в стеке, заносится в поле параметров <name>. Если же пе¬
ред ним стоит АТ, в стек заносится адрес поля параметров слова <name>. (Подробнее QUAN обсуж¬
дается на стр.82). См. также CQUAN; QUAN; 2QUAN.
4VARIABLE ”four-variable” MMS
( ~)
Слово-описатель, которое создает 64-битовую переменную обычно для чисел с плавающей запятой.
При использовании в форме
4VARIABLE <name>
формирует статью в словаре с именем <name> и резервирует 64 бита в поле параметров для хране¬
ния числа с плавающей запятой. Когда <name> исполняется, адрес поля параметров <name>, где
хранится число с плавающей запятой, засылается в стек. 4VARIABLE не обязательно инициализи¬
рует значение переменной. См. также VARIABLE; CVARIABLE; 2VARIABLE. Противостоит
4CONSTANT.
64* ”sixty-four-times” Умножить на 64” MMS
( nl - n2)
Умножает nl на 64, результат n2 кладет в стек.
79-STANDARD ”79-standard” "стандарт-79" 79REQ
( --)
Используется для проверки того, отвечает ли используемая версия стандарту Форт-79. Если версия
нестандартная, слово либо не будет узнано, либо будет дано сообщение об ошибке. См. также
Форт-83,
8* ’’eight-times” "умножить на 8” MMS
( nl - n2)
Умножает число nl на 8, результат n2 заносит в стек.
: ’’colon” "двоепючие” 83REQ 79REQ
( -)
Слово-описатель, которое формирует слово-двоеточие. При исполнении в форме
: <name> ... ;
формирует статью словаря с именем <name> в текущем контекстном словаре и переключает STATE
в режим компиляции, вызывая компиляцию последующих слов и чисел из входного потока, <name>
не может быть найдено в словаре до тех пор, пока не будет успешно обработан соответствующий
оператор ; или ;CODE. Оператор : устанавливает такой порядок поиска, при котором первым про¬
сматривается контекстный словарь, который заменен словарем компиляции. Словарь компиляции
не изменяется. Слово-двоеточие — главное средство программирования на Форте. См. также ;,
;CODE.
; ”semi-colon” ”пючка e загшпюй" 83REQ 79REQ
( -)
Прекращает компиляцию описания-двоеточие. При исполнении в форме
: <name> ... ;
разрешает нахождение имени <name> в словаре, компилирует EXIT (или зависящее от системы
слово, которое выполняет аналогичное действие), устанавливает STATE в режим исполнения. При
244
использовании <name> его исполнение завершается словом EXIT и управление передается слову,
которое обратилось к <name>. См. также :,;CODE.
;CODE ”semi-colon-code” 83ASM 79ASM
( ~)
Слово-описатель-терминатор, которое может использоваться только в режиме компиляции. При ис¬
полнении в форме
: <namex> ... CREATE ... ;CODE ... END-CODE
прерывает компиляцию, завершает работу слова-описателя <namex> и исполняет слово
ASSEMBLER. Это разрешает использование мнемоники ассемблера для компиляции программы
между ;CODE и END-CODE. При исполнении <namex> для формирования <name>
<namex> <name>
поле программы <name> будет содержать адрес машинной программы, которая размещена сразу по¬
сле ;CODE в <namex>. Таким образом, ;CODE позволяе^ создавать слова-описатели, которые фор¬
мируют производные слова, функция которых определена последовательностью мнемокодов ассемб¬
лера, размещенных между ;CODE и END-CODE в описании <namex>. По своему действию подобно
DOES>. См. также CODE; END-CODE; CREATE; DOES>; и обсуждение на стр.228.
< ”less-than” "меныие чем" 83REQ 79REQ
( nl n2 — флаг)
Сравнивает числа nl и n2 и засылает в стек флаг истинно
<# “less-sharp” 83REQ 79REQ
( -)
Начинает преобразование числа в форматированную выходную строку. Смотри, например, #. См.
также #>; #S; HOLD; SIGN.
<- ”less-than-or-equal” "меньше или равно" MMS
( nl n2 -- флаг)
Сравнивает числа nl и n2 и засылает в стек флаг истинно, если nl меньше или равно n2.
<> ”not-equal” "не равно" 83UNC 79RES MMS
( nl n2 -- флаг)
Сравнивает числа nl и n2 и засылает в стек флаг истинно, если nl не равно n2.
<CMOVE ”reverse-c-move” "реверсив
Hoe-c-move" 83UNC 79RES MMS
( адр1 адр2 n —)
Копирует n байтов, начиная с адреса ”адр1”, и засылает их, начиная с ”адр2”. Перенос начинается
со старших адресов и продолжается в направлении младших. (Если n равно 0, ни один байт не пе¬
реносится.) В Форт-83 это слово имеет имя CMOVE>, а число переносимых байтов задается числом
без знака. См. также CMOVE; MOVE.
<S# ”less-s-sharp” TXT
( d — n d)
Извлекает старшее число (часть, содержащую знак) n из d и начинает преобразование в формати¬
рованную выходную строку. Описано для упрощения вывода чисел двойной длины со знаком.
: <S# SWAP OVER DABS <# ;
- ’’equals” "равно" 83REQ 79REQ
( nl n2 -- флаг)
Сравнивает числа nl и n2 и засылает флаг истинно, если nl равно n2.
> ”greater-than” "больше" 83REQ 79REQ
( nl n2 -- флаг)
Сравнивает числа nl и n2 и засылает флаг истинно, если nl больше n2.
>- ”greater-than-or-equal” "больше или равно" MMS
( nl n2 -- флаг)
Сравнивает числа nl и п2 и засылает флаг истинно, если nl больше или равно n2.
>BODY ”to-body” 83REQ
( адр1 — адр2)
Исходный адрес ”адр1” — адрес поля программы слова. В стек засылается ”адр2” — адрес поля па¬
раметров этого слова. В Форт-83
‘ <name> >BODY
выдает тот же адрес, что и
‘ <name>
в Форт-79.
245
>IN "to-in" 83REQ 79REQ
( - адр)
Переменная, которая содержит номер текущего байта во входном потоке, где происходит сейчас ин¬
терпретация. Если источником входного потока является массовая память, команда
BLK @ BLOCK >IN @ +
засылает в стек текущий адрес интерпретации; в Форт-83 при вводе с клавиатуры эквивалентный
адрес вычисляется по команде:
>IN @ TIB +
>LINLK "to-link" 83FLD
( адр1 - адр2)
Исходное число ”адр1” — адрес поля программы слова, результат ”адр2” — адрес поля связи этого
слова.
>NAME "to-name" 83FLD
( адр1 -- адр2)
Исходное число ”адр1” — адрес поля программы слова, результат ”адр2” — адрес поля имени этого
слова.
>R "to-r" 83REQ 79REQ
( n -)
Передает n из стека параметров в стек возвратов. Поскольку слово >R изменяет стек возвратов, оно
в норме должно использоваться в паре с R> до завершения описания-двоеточие. Противостоит
R@.
? ’*question-mark” "знак вопроса" 79REQ
( адр --)
Отображает число, лежащее по адресу ”адр”. Использует формат оператора .. Эквивалентно @ . .
7BRANCH ”question-branch” условное
ветвление” 83SYS VAR
( флаг —)
При исполнении в форме
: <name> ... COMPILE 7BRANCH ... ;
в текст описания-двоеточие компилируется операция условного перехода. При исполнении, если
флаг в стеке имеет значение ложно, передача управления производится так же, как при BRANCH.
Если же флаг имеет значение истинно, исполнение производится, начиная со слова, скомпилиро¬
ванного сразу после адреса ветвления, который следует за 7BRANCH. См, стр.211, где имеются бо¬
лее подробное описание и примеры. Обычно в программировании 7BRANCH не используется, в не¬
которых версиях применение ограничено описаниями IF, WHILE и UNTIL.
?DUP ”question-dupe” 83REQ 79REQ
( n — n n) или (0 — 0)
Дублирует в стеке число n, если оно не равно 0.
?KEY "question-key” MMS
( - с)
Проверяет, была ли нажата какая-либо клавиша, и засылает в стек ASCII-код клавиши или 0, если
ни одна клавиша не нажата. ?КЕУ не ожидает нажатия клавиши.
@ ’*fetch” "извлечь" 83REQ 79REQ
( адр — nb)
Заносит в стек число одинарной длины, хранящейся по адресу ”адр”. См. C@; 2@; 4@.
ABORT ”abort’* 83REQ 79REQ
( -)
Очищает стек параметров и выполняет функцию QUIT, возвращая управление клавиатуре терми¬
нала. Не выдается никаких сообщений, даже ”ок”. Для обсуждения роли ABORT в качестве цент¬
рального слова внешнего интерпретатора Форт см. также описание QUIT на стр.368.
ABORT” ’’abort-quote” 83REQ VAR
( флаг —)
Отображает сообщение в зависимости от флага в стеке и прерывает исполнение программы. При ис¬
пользовании в форме
: <name> ... ABORT*’ ссс” ... ;
компилирует текст ”ссс” вплоть до разграничителя ” (двойная кавычка) в описании <name>. После
ABORT” необходим пробел, который не включается в ”ссс”; перед разграничителем ” пробела не
требуется. При исполнении <NAME> ABORT” предполагает наличие флага в стеке. Если флаг не
246
равен 0, скомпилированный текст будет отображен, а затем исполнится последовательность команд,
включающая ABORT. Если флаг будет иметь значение ложно, вышеуказанные процедуры будут
опущены и исполнение будет продолжено. Точная форма отображения может зависеть от версии,
может отображаться имя слова, в котором использовано <name>, или только текст сообщения.
ABS "absolute" "абсолютная величина" 83REQ 79REQ
( nl - n2)
Зысылает в стек значение абсолютной величины числа nl, т. e. то же значение числа, но непремен¬
но положительное. Исключение составляет Форт-83, где для числа -32.768 знак не меняется (оно в
норме не меняется и в Форт-79).
ACASE "a-case" MMS
(симв —)
Начинает алфавитно-цифровую CASE-структуру
ACASE А В" <namel> <name2> <name3> OTHERWISE ...CASEND
Список символов (здесь А, пробел и В), завершаемый двойной кавычкой, используется для выбора
того, какое из списка Форт-слов, следующих за ”, будет исполнено. Таким образом, если в стеке
символ ”А” (ASCII 65), управление будет передано слову <namel>; аналогично, если в стеке код
пробела (ASCII 32), управление передается <name2>. Если выбранное слово исполнено, управление
передается слову, следующему за CASEND. Если код символа в стеке не содержится в списке сим¬
волов, управление передается слову, следующему за OTHERWISE (если оно используется) или за
CASEND. См. также NCASE. Обсуждение CASE-структур смотри на стр.98.
ALLOT "allot” "зарезервировать" 83REQ 79REQ
( n ~)
Резервирует место в словаре для n байтов, т.е. адрес первой свободной позиции в словаре, который
засылается в стек оператором HERE, увеличивается на n.
.AND "and" "И" 83REQ 79REQ
( nl n2 — n3)
Выполняет логическую операцию побитного ”И” для чисел nl и n2, т.е. каждый бит числа nl срав¬
нивается с эквивалентным битом числа n2 и, если любой из битов равен 0, в соответствующий бит
числа n3 заносится 0. Итак (в двоичном представлении),
110 100 AND
зашлет в стек двоичный код 100. См. также O^R; XOR.
ARRAY "array" "массив" MMS
( n ~)
Слово-описатель для создания линейных массивов (векторов) чисел одинарной длины. При исполь¬
зовании в форме
n ARRAY <name>
формирует статью в словаре с именем <name> и резервирует место для n+l числа одинарной длины.
Когда <name> используется в форме:
n <name>
В стек засылается адрес (n+l)-ro элемента массива См. также CARRAY; 2ARRAY; 4ARRAY.
ASC "ask-e" MMS
( адр — п)
Засылает в стек ASCII-код первого символа счетной строки, хранящейся по адресу ”адр”.
ASSEMBLER "assembler" "ассемблер" 83ASM 79ASM
( ~)
Заменяет первый контекстный словарь, где производится поиск, на словарь ASSEMBLER. В Форт-79
ассемблер выбирается как CONTEXT-словарь.
AT "at" MMS
( ~)
При использовании в форме
AT <name>
засылает в стек содержимое слова QUAN-типа с именем <name>. См. также QUAN; 2QUAN;
4QUAN. Противостоит IS. См. обсуждение QUAN на стр.82.
BASE "base" "основание" 83REQ 79REQ
( адр --)
Переменная, содержащая основание системы счисления, которое используется при вводе и выводе
чисел. Так
2 BASE !
247
выберет двоичную систему счисления для ввода-вывода. В Форт-83 основание системы счисления
должно лежать в пределах 2 — 72, а в Форт-79 это диапазон 2 — 70. См. также DECIMAL;
OCTAL; HEX; BINARY.
BASE? "base-question” TXT
( ~)
Отображает текущее значение BASE в десятичном представлении без изменения BASE.
: BASE? BASE @ DUP DECIMAL . BASE ! ;
BEGIN "begin” I, С 83REQ 79REQ
( ~)
Отмечает начало структуры бесконечного цикла. При использовании в форме
: <name> ... BEGIN ... флаг UNTIL ... ;
или
: <name> ... BEGIN ... флаг WHILE ... REPEAT ... ;
компилирует структуру бесконечного цикла. Когда <name> исполняется, слова между BEGIN и
UNTIL будут выполняться повторно, пока флаг в стеке соответствует состоянию истинно.
Исполнение слов между BEGIN и REPEAT будет повторяться до тех пор, пока флаг в стеке перед
исполнением WHILE равен истинно. Слова после UNTIL или REPEAT будут исполняться по за¬
вершении цикла.
BINARY **binary” TXT
( -)
Выбирает двоичное представление чисел при вводе-выводе.
: BINARY 2 BASE ! ;
См. также DECIMAL; HEX; OCTAL.
BL ”b-l” 83CNT 79RES MMS
( - 32)
Заносит в стек ASCII-код пробела (десятичное число 32)
BLANK ’’blank” 83CNT 79RES MMS
( адр n —)
Записывает в n байтов памяти, начиная с адреса ”адр”, ASCII-код пробела (десятичное 32). При n,
равном 0, ничего не делается. (В стандарТе Форт-79 этот оператор имеет имя BLANKS, но так как
для этого нет никакой исторической причины и поскольку несколько версий используют BLANKS,
мы предполагаем, что это имя является типографской ошибкой в тексте стандарта.)
BLK ”b-l-k” 83REQ 79REQ
( - адр)
Переменная, содержащая номер блока в массовой памяти, который в данный момент интерпретиру¬
ется. Если величина BLK равна 0, источником входного потока является клавиатура.
BLOCK ’’block” "блок" 83REQ 79REQ
( n — адр)
Засылает в стек адрес первого байта буфера, содержащего блок n. Если блок еще не в памяти, то он
будет передаваться из массовой памяти в блочный буфер, обращение к которому произошло раньше
других. (В Форт-83 используется не обязательно буфер, к которому дольше всего не обращались,
как в случае Форт-79, но большинство реализаций следует версии Форт-79.) Если блок, занимаю¬
щий этот буфер, не является блоком n и он был подвергнут операции UPDATE (т.е. модифициро¬
ван), его содержимое будет перенесено в массовую память, прежде чем блок n будет считан в бу¬
фер. Если правильное чтение или запись в массовую память не возможны, будет дано сообщение об
ошибке. Дальнейшее обсуждение слова BLOCK см. на стр.136.
BODY> ”from-body” 83FLD
( адр1 - адр2)
Исходный код ”адр1” — адрес поля параметров слова, результат, ”адр2”,— адрес поля программы.
BRANCH ’’branch” С 83SYS
( -)
При использовании в форме
: <name> ... COMPILE BRANCH ... ;
компилируется оператор безусловного перехода. Адрес ветвления должен быть скомпилирован сразу
после оператора безусловного перехода. Обычно не используется при составлении программ, а толь¬
ко в словах ELSE и REPEAT. См. также 7BRANCH.
BUFFER ’*buffer” "буфер" 83REQ 79REQ
( n — адр)
248
Присваивает номер блока n блочному буферу, обращение к которому произошло раньше других. В
стек при этом засылается адрес первого байта этого буфера. В Форт-79 блок не читается из массо¬
вой памяти, он может читаться или нет в Форт-83. Если буфер был помечен для записи на диск
(UPDATE), его содержимое будет перенесено в массовую память. Если правильная передача в мас-
совук> память невозможна, будет дано сообщение об ошибке. Слово BUFFER подобно BLOCK, за
исключением того, что не обязательно производится обмен с массовой памятью. См. также
BLOCK.
С! ”c-store” ’С-присвоить” 83REQ 79REQ
( адр --)
Записывает младший байт числа n в байт с адресом ”адр”. См. также !.
С, "C-comma” 'V-запятая" 83REQ 79REQ
( n ~)
Записывает младший байт числа n (который в норме меньше 256) в байт первой свободной позиции
словаря (адрес, засылаемый в стек оператором HERE) и добавляет к указателю словаря 1, чтобы
зарезервировать место для скомпилированного байта. См. также , .
C@ ”C-fetch” "извлечь байт" 83REQ 79REQ
( адр - n)
Заносит в стек байт, лежащий по адресу ”адр”. Результатом является число, старший байт которого
равен 0, а младший — извлеченному байту. См. также @.
CARRAY ”с-аггау” "массив байтов" TXT MMS
( n -)
Слово-описатель, которое создает линейный массив (вектор) с однобайтовыми элементами. При ис¬
пользовании в форме
п CARRAY <name>
создается статья словаря с именем <name> и резервируется место для n+l байта. Когда <name> ис¬
пользуется в форме
n <name>
в стек заносится адрес (n+l)-ro элемента.
: CARRAY CREATE ALLOT DOES> + ;
См. также 2CARRAY; ARRAY; ECARRAY.
CASEND "case-end" MMS
( ~)
Отмечает конец ACASE- или NCASE-структуры в MMSFORTH. См. также ACASE; NCASE.
CCONSTANT "c-constant” TXT MMS
( b ~)
Слово-описатель, которое создает байтовые константы. При использовании в форме
п CCONSTANT <name>
формирует в словаре статью с именем <name> и записывает байт из стека в поле параметров
<name>. При исполнении байт из поля параметров заносится в стек.
: CCONSTANT CREATE С, DOES> C@ ;
CFA "c-f-a" FIG VAR
( адр1 - адр2)
Исходный код ”адр1” является адресом поля параметров, результирующий ”адр2” — адресом поля
программы.
CHR$ "c-h-r-string” MMS
( n — адр)
Создает однобайтовую счетную строку, которая содержит символ, ASCII-код которого лежит в сте¬
ке. Строка записывается в PAD, адрес которого ”адр” заносится в стек.
CMOVE ”c-move” 83REQ 79REQ
( адр1 адр2 n —)
Копирует п байтов, начиная с адреса ”адр1”, и укладывает их, начиная с адреса ”адр2”. Перенос
осуществляется, начиная с младших адресов, и продолжается в сторону старших. В Форт-79, если n
равно 0 или отрицательно, ничего не переносится. См. также <MOVE; CMOVE>; MOVE.
CMOVE> "c-move-up” 83REQ
( адр1 адр2 u --)
Копирует u байтов, начиная с адреса ”адрГ\ в область памяти, начинающуюся с адреса ”адр2”.
Перенос происходит, начиная с больших адресов в направлении малых. Если u равно 0, ничего не
249
переносится. CMOVE> полезно для перемещения байтов в направлении ’’вверх”, когда области об¬
мена перекрываются. (В Форт-79 имеется имя <CMOVE.) См. также CMOVE, MOVE.
CODE "code” "код" 83ASM 79ASM
( -)
Слово-описатель, которое создает ассемблерные описания. При использовании в форме
CODE <name> ... END-CODE
формирует статью словаря с именем <name> и делает ASSEMBLER контекстным словарем, чтобы
разрешить применение мнемоники ассемблера. При исполнении <name> выполняется машинная
программа. Слова, описанные таким образом называются CODE-описаниями или CODE-словами.
COMPILE "compile” "скомпилировать" 83REQ 79REQ
( ~)
Обычный формат использования:
: <namel> ... COMPILE <name2> ... ;
Когда компилируется <namel>, в словарь вслед за адресом программы COMPILE компилируется ад¬
рес поля программы <name2>. <namel> является обычно словом немедленного исполнения, а
<name2>, как правило, не является таковым. Когда <namel> исполняется (обычно в описании типа
двоеточие), адрес поля программы <name2> компилируется в верхнюю ячейку словаря. Во многих
версиях Форта LITERAL может скомпилировать слово LIT в другие слова, так как команда
COMPILE LIT используется в описании LITERAL.
CONSTANT "constant" "константа" 83REQ 79REQ
( n -)
Слово-описатель, которое создает константы одинарной длины. При использовании в форме
n CONSTANT <name>
формирует в словаре статью с именем <name> и компилирует n в поле параметров <name>. Когда
<name> исполняется, в стек заносится число одинарной длины n. См. также 2CONSTANT; 4
CONSTANT; CCONSTANT. Противоположно VARIABLE.
CONTEXT "context" "контекст" 83SRC 79REQ
( -- адр)
Заносит в стек адрес переменной, которая определяет контекстный словарь, подлежащий просмотру
первым. Форт-83 содержит предварительное предложение, в котором не использовано слово
CONTEXT (или CURRENT), но предусматривается альтернативный способ задания порядка про¬
смотра словаря. См. также CURRENT; DEFINITIONS; VOCABULARY.
CONTROL *’control" Управление" TXT
( n... ~)
Слово-описатель, которое создает слово, предназначенное для посылки одного или более управляю¬
щих кодов на активное выходное устройство. При использовании в форме
n...CONTROL<name> (1)
формирует статью в словаре с именем <name> и компилирует один или более управляющих кодов
ASCII из стека. Когда <name> исполняется, управляющие коды, скомпилированные в его поле пара¬
метров, посылаются на активное в данный момент выходное устройство.
: CONTROL CREATE DEPTH DUP С, 0 DO DEPTH ROLL С, LOOP1)
DOES> DUP DUP C@ + SWAP DO I 1 + C@ EMIT LOOP ;
Дальнейшие пояснения можно найти на стр 326.
CONVERT "convert” "преобразовать" 83REQ 79REQ
( dl адр1 — d2 адр2)
Преобразует в соответствии с величиной BASE несчетную ASCII-строку цифр в число, d2 является
результатом преобразования каждого из символов (цифр) в строке, начиная с адреса ”адр+1”, в
число и аккумуляции каждого числа, умноженного на величину BASE, в dl (dl * норме равно 0).
Преобразование продолжается до тех пор, пока не встретится непреобразуемый символ. Адрес этого
символа засылается в качестве ”адр2”. Таким образом, если стек содержит число двойной длины
55, а строка имеет вид ”11х”, тогда CONVERT занесет в стек число двойной длины 5511, а адрес
”х” будет занесен в качестве ”адр2”.
COPIES, ”copies” "скопировать" TXT
( n 1 n2 n3 ~)
1 Описание предполагает, что перед обращением (1) стек пуст.— Прим. перев.
250
Копирует серию из ”пЗ” смежных блоков, начиная с блока ”п1”, и укладывает их, начиная с блока
”п2”. Копирование начинается с блока с наименьшим номером и продолжается в направлении по¬
следнего блока. Смотри описание на стр.310.
COPIES> ”copies-up” TXT
( nl n2 n3 —)
Копирует серию из n3 смежных блоков, начиная с nl, и записывает их, начиная с n2. Перенос на¬
чинается с блока, имеющего наибольший номер, и продолжается в направлении первого блока.
Описание смотри на стр.310.
COPY ’*copy” TXT
( nl n2 -)
Копирует блок ”п1” в буфер для блока ”п2” и производит операцию UPDATE для этого буфера.
Когда выполняется слово FLUSH или когда буфер ”п2” используется повторно, вышеописанная
операция имеет эффект копирования блоков ”п1” в ”п2”. (Чтобы предотвратить непредсказуемые
результаты, до COPY следует применить операторы FLUSH или EMPTY-BUFFERS.)
COUNT ’’count” "подсчет” 83REQ 79REQ
( адр1 -- адр2 n)
Засылает в стек число символов в счетной строке, лежащей по адресу ”адр1”. ”адр2” равен
”адр1+1” и указывает на начало текста, а ”п”- длина этого текста. COUNT засылает в стек адрес и
число-счетчик, необходимые для работы слова TYPE. Таким образом
COUNT TYPE
отобразит счетную стрбку, лежащую по адресу ”адр”.
: COUNT DUP C@ SWAP 1+ ;
CQUAN ”c-quan” MMS
( ~)
Слово-описатель, которое создает слова QUAN-типа с байтовым содержимым. При использовании в
Форме
CQUAN <name>
формирует в словаре статью с именем <name> и резервирует место в поле параметров <name> для
записи байта. Когда исполняется само <name>, код, который в нем содержится, заносится в стек.
Если перед ним стоит IS, число из стека записывается в поле параметров <name>. Если перед ним
стоит АТ, в стек заносится адрес поля параметров <name>. См. также QUAN; 2QUAN; 4QUAN.
См. стр.82, где дано описание слова типа QUAN.
CR ”с-г” "возврат каретки" 83REQ 79REQ
( ~)
Посылает на активное в данный момент внешнее устройство коды ’’возврат каретки” и ’’перевод
строки”. На дисплее CR-помещает обычно курсор в начало следующей строки.
CREATE ”create” "создать" 83REQ 79REQ
( ~>
Слово-описатель, создающее статью в словаре. При использовании в форме
CREATE <name>
формирует статью с именем <name> без резервирования места в поле параметров <name>. При ис¬
полнении <name> адрес первого байта поля параметров <name> заносится в стек. Поле параметров
<name> не защищено, если не использованы слова ALLOT, С, или , . CREATE может также ис¬
пользоваться в описаниях типа двоеточие в форме
: <nanex> ... CREATE ... ;
ИЛИ
: <namex> ... CREATE ... DOES> ... ;
ИЛИ
: <namex> ... CREATE ... ;CODE ... END-CODE,
чтобы сформировать новое слово-описатель <namex>. Например, VARIABLE можно описать как:
: VARLAJ3LE CREATE 2 ALLOT ;
ИЛИ
: VARIABLE CREATE 0 , ;
чтобы присвоить переменной нулевое начальное значение.
CRS ”c-rs” тхт
( n ~)
Посылает n пар кодов ’’возврат каретки” и ’’перевод строки” на активное внешнее устройство. Это
перемещает текст на дисплее на n строк вверх.
251
: CRS 0 DO CR LOOP ;
CRT ”c-r-t” MMS
( ~>
Переключает вывод только на экран. См. также PCRT; PRINT.
CURRENT "current” ”текущий" 83SRC 79REQ
( -- адр)
В Форт-79 это переменная, определяющая контекстный словарь, в котором будут описываться но¬
вые слова. В Форт-83 CURRENT является словом ’’системного расширения” и не входит в обяза¬
тельный список. Порядок поиска слова здесь не задан, за исключением слов, входящих в экспери¬
ментальный список. См. также CONTEXT; DEFINITIONS; VOCABULARY. Подробнее разъясне¬
ние см. на стр. 196.
CVARIABLE ”c-variable” ’'С-переменная" MMS
( ~)
Слово-описатель, которое создает байтовые переменные. При использовании в форме
CVARLABLE <name>
формирует в словаре статью с именем <name> и резервирует байт в его поле параметров. В качест¬
ве начального значения засылается нуль. При исполнении <name> в стек засылается адрес его поля
параметров.
: CVARUUBLE CREATE 0 С, ;
D#IN ”d-number-in” MMS
( - d)
Отображает запрос ”?” и ожидает ввода числа. Число заносится в стек в виде кода двойной длины.
При ошибке ввода последует запрос ”?Redo” и вы сможете сделать еще одну попытку. В HI# и
#PT заносятся соответствующие коды. См. #IN.
D* ”d-times” "D-умножить” MMS VAR
( dl d2 - d3)
Умножает dl на d2 и заносит в стек произведение двойной длины d3.
D*/ ”d-tlmes-divide” '*D-умножить-разделить” MMS
( dl d2 d3 - d4)
Умножает dl на d2 (произведение имеет 64 бита) и делит произведение на d3. Округленное в на¬
правлении нуля значение частного двойной длины, d4, заносится в стек. См. также */.
D*/MOD ”d-times-divide-mod” ”D-умножить-разделить с остатком” MMS
( dl d2 d3 - d4 d5)
Умножает dl на d2 (промежуточный результат имеет 64 бита) и делит произведение на d3, засылая
в стек остаток двойной длины d4 и целое, округленное в направлении нуля частное двойной длины
d5. См. также */MOD.
D+ ”d-plus” "D-плюс” 83REQ 79REQ
( dl d2 - d3)
Складывает d2 и dl, а сумму двойной длины d3 засылает в стек.
D- ”d-minus” "D-минус” 83DBL 79DBL
( dI d2 - d3)
Вычитает d2 из dl и засылает значение разности двойной длины в стек.
D. ”d-dot” "D-точка" 83DBL 79DBL ( d -)
В соответствии со значением слова BASE отображает величину ”d” (включая знак минус, если чис¬
ло отрицательно) в позиции на экране, отмеченной курсором. После вводится пробел. См. также .
(точка).
D.R ”d-dot-r” "D-mo4Ka-R" 83DBL 79DBL
( d n ~)
Отображает число d в поле длиной n позиций так, что младшая цифра занимает в поле самое пра¬
вое положение. Если число d отрицательно, перед ним ставится знак минус. В Форт-83, если число
символов, необходимых для отображения d, больше n, дается сообщение об ошибке. В Форт-83 не
разрешена также длина поля менее 1. В Форт-79 при n меньше 1 предшествующие числу пробелы
опускаются, а при требуемом числе символов для отображения d больше n не дается сообщения об
ошибке. (В большинстве версий Форт-79 результат будет напечатан без лидирующего пробела.)
Смотри .R .
D/ ”d-divide” "D-разделить” MMS
( dl d2 - d3)
Делит dl на d2 и засылает частное двойной длины, округленное в сторону 0, в стек.
252
D/MOD ”d-divide-mod” ”D-разделить с остатком" MMS
( dl d2 - d3 d4)
Делит dl на d2 и засылает в стек остаток двойной длины d3 и частное двойной длины d4, округ¬
ленное в сторону нуля.
DO- "d-zero-equals” "D равно нулю" 83DBL 79DBL
( d -- флаг)
Сравнивает число d с нулем и засылает в стек флаг истинно, если d равно нулю.
D2/ ”d-two-divide” "D-разделить на 2" 83DBL
( dl - d2)
Делит dl на 2 и засылает в стек частное двойной длины d2. Используется деление с нижней грани¬
цей.
D< ”d-less-than” "D-меньше” 83REQ 79REQ
( dl d2 — флаг)
Сравнивает числа dl и d2 и засылает в стек флаг истинно, если dl меньше d2.
D- ”d-equal” "D-равно" 83DBL 79DBL
( dl d2 — флаг)
Сравнивает числа dl и d2 и засылает в стек флаг истинно, если dl равно d2.
DABS "d-absolute” . 83DBL 79DBL
( dl - d2)
Засылает в стек абсолютную величину числа двойной длины со знаком dl. Таким образом, d2 будет
числом двойной длины, имеющим ту же величину, что и dl, но всегда положительным. Исключе¬
нием является случай, когда в Форт-83 dl равно -2.147.483.648. Для этого числа знак не изменяет¬
ся. (Знак этого числа не изменяется и в большинстве версий Форт-79.)
DARRAY ”d-array” *’D-массив” MMS
( n ~)
Слово-описатель, создающее линейный массив (вектор) чисел двойной длины. При использовании в
форме
n DARRAY <name>
формирует в словаре статью с именем <name> и резервирует место для n+l числа двойной длины.
Когда <name> исполняется в форме:
n <name>
в стек засылается адрес (n+l)-ro элемента. См. также 4ARRAY; ARRAY; CARRAY. Противопо¬
ложно 2ARRAY.
DECIMAL ’’decimal” 83REQ 79REQ
( ~)
Устанавливает для ввода-вывода десятичное представление чисел. Заносит 10 (десятичное) в пере¬
менную BASE.
DEFINITIONS ’’definitions” "описания" 83REQ 79REQ
( ~)
Задает контекстный словарь, в котором компилируются описания. Именно этот словарь просматри¬
вается при поиске первым. В Форт-83 средства для изменения порядка просмотра словаря не пре¬
дусмотрены. Это реализовано в Форт-79 путем присвоения переменной CURRENT значения пере¬
меной CONTEXT. Например, выполнение команды
ASSEMBLER DEFINITIONS
приведет к тому, что последующие описания окажутся в контекстном словаре ASSEMBLER. См.
также VOCABULARY.
DEPTH ’’depth” "глубина" 83REQ 79REQ
( - n)
Засылает в стек число одинарной длины, равное числу кодов, лежащих в стеке до исполнения слова
DEPTH. Если стек пуст, n равно 0.
DMAX ”d-max” 83DBL 79DBL
( dl d2 - d3)
Заносит в стек число d3, которое является большим из dl и d2. См. также DMIN; МАХ
DMIN ”d-min” 83DBL 79DBL
( dl d2 - d3)
Засылает в стек d3, которое является меньшим из dl и d2. См. также DMAX; MIN.
DNEGATE ”d-negate” 83REQ 79REQ
( dl - d2)
253
Меняет знак числа dl на обратный и заносит результат в стек, d2 является дополнением dl по мо¬
дулю два. (т.е. 0 минус dl).
DO ”do” ''выполнить” I, С 83REQ 79REQ
( nl n2 --)
Подготавливает начало цикла do-loop. При использовании в форме:
: <name> ... nl n2 DO ... LOOP ... ;
ИЛИ
: <name> ... nl n2 DO ... приращение +LOOP ... ;
подготавливает начало цикла do-loop. Когда <name> исполняется, DO берет числа nl и n2 из стека
и использует n2 в качестве начального значения индекса цикла, а nl — в качестве предела, кото¬
рый определяет условие завершения цикла do-loop. Чтобы выяснить отличия между условиями за¬
вершения цикла в Форт-79 и Форт-83, смотри описания LOOP и +LOOP. Любой цикл do-loop вы¬
полняется по крайней мере один раз. Допускается вложение циклов do-loop, с помощью слова
LEAVE можно прервать цикл до достижения предела.
DOES> "does" "выполняет” I, С 83REQ 79REQ
( -- адр)
Определяет действие слова, сформированного словом-описателем, на фазе исполнения, Исполь¬
зуется в форме
: <namex> ... <create> ... DOES> ... ;
где <create> соответствует CREATE или другому слову-описателю, которое использует CREATE, а
<namex> — новое слово-описатель. (В действительности Форт-79 определяет, что <create> должно
быть словом CREATE, в то время как Форт-83 допускает вышеописанный вариант. Большинство
версий Форт-79, однако, позволяют использование либо CREATE, либо слова-описателя с ним
внутри.) DOES> отмечает начало описания <namex>, которое определяет поведение его производ¬
ных слов. Таким образом, когда <namex> исполняется:
<namex> <name>
формируется в словаре статья с именем <name>, а поведение слова <name> при исполнении задает¬
ся последовательностью слов между DOES> и ; в описании <namex>. DOES> можно использовать
только внутри описания типа двоеточие. Например, опишем CONSTANT:
: CONSTANT CREATE , DOES> @ ;
Более подробное описание смотри на cmp.l52. См. также ;CODE.
DROP "drop” '*удалить" 83REQ 79REQ
( n ~)
Удаляет код n из стека.
DU* "d-u-times” ”d-и-умножить” MMS
( udl ud2 -- uq)
Умножает udl на ud2 (оба являются числами двойной длины без знака) и засылает в стек 64-бито¬
вое их произведение uq.
DU/MOD ”d-u-mod” MMS
( uq udl — ud2 ud3)
Делит uq (64-битовое число без знака) на udl (число двойной длины без знака) и засылает остаток
ud3 и частное ud4.
DU< ”d-u-less” ”й-и-меньше" 83DBL 79DBL
( udl ud2 — флаг)
Сравнивает udl и ud2 (оба числа двойной длины без знака) и засылает в стек флаг истинно, если
udl меньше ud2.
DUMP ”dump” 83CNT 79RES TXT MMS VAR
( адр n --)
Распечатывает содержимое n байтов памяти, начиная с адреса ”адр”. Каждая строка начинается с
пропечатки адреса первого байта, в остальном все зависит от версии программы. (Приведенное опи¬
сание предполагает наличие в стеке начального адреса и числа 16-байтовых строк, а не числа бай¬
тов. Это описание отображает байты в шестнадцатеричном представлении в ASCII-форме.)
: DUMP ( адр n ~) CR BASE @ >R HEX
16 * OVER + SWAP DO I 0 <# # # # # #> TYPE 2 SPACES
16 0 DO I 4 MOD NOT IF SPACE THEN I J + C@
0 <# # # #> TYPE SPACE
LOOP CR 6 SPACES
16 0 DO I 4 MOD NOT IF SPACE THEN I J + C@ DUP
254
31 > OVER 127 < AND IF EMIT 2 SPACES
ELSE DROP 3 SPACES
THEN
LOOP CR
16 +LOOP R> BASE ! ;
DUP "dupe" ”задублировать” 83REQ 79REQ
( n — n n)
Дублирует в стеке число одинарной длины n.
E ”е” (for ”edit”) (для "EDIT”) TXT MMS
( ~)
Редактирует блок, номер которого записан в SCR (обычно это блок, только что редактированный
или выведенный на экран).
ECARRAY "еггог-с-аггау" TXT
( n ~)
Слово-описатель, создающее линейный массив (вектор) с байтовыми элементами. При использова¬
нии в форме
n ECARRAY <name>
формирует в словаре статью с именем <name> и резервирует в памяти n+l байт. При использовании
<name> в форме
n <name>
в стек засылается адрес (n+l)-ro элемента. Если n больше, чем число элементов в <name>, или n
меньше 0, выдается сообщение об ошибке.
: ECARRAY CREATE DUP , 2 + ALLOT
DOES> DUP @ 3 PICK U<
IF + 2+ ELSE ." Index error” ABORT THEN ;
Почти идентичен CARRAY, за исключением контроля ошибки.
EDIT "edit" ”редактировать” TXT MMS VAR
( n ~)
Активизирует редактор Форта и редактирует блок n. См. гл. 12 и 13.
EDITOR ’’editor” ”редактор" 83CNT 79RSV
( --)
Делает так, чтобы контекстный словарь EDITOR просматривался первым. (Многие версии Форта не
используют отдельный контекстный словарь для редактора.)
ELSE ”else” Гиначе" I, С 83REQ 79REQ
( ~>
Отмечает начало альтернативной ветви программы. При использовании в форме
: <name> ... флаг IF ... JELSE ... THEN ... ;
компилирует оператор безусловного перехода, чтобы продолжать исполнение сразу после оператора
THEN. Когда <name> исполняется, IF предполагает наличие* флага в стеке. Если флаг не равен 0,
будут исполнены слова между IF и ELSE с продолжением после THEN, если же значение флага
равно 0, управление будет передано словам между ELSE и THEN с продолжением после THEN.
EMIT ”emit” 83REQ 79REQ
( n ~)
Посылает символ, код которого лежит в младшем байте числа n, на активное выходное устройство
(обычно дисплей), n, как правило, меньше 256. Форт-83 требует, чтобы только младшие 7 битов (т.
e. ASCII-код) отображались, но это ограничение игнорируется в большинстве версий.
EMPTY-BUFFERS "empty-buffers” "очистить буферы” 83CNT 79REQ
( ~)
Удаляет флаги спасения и стирает все коды приписки буферов к блокам. Помеченные для записи
ранее блоки не будут записаны в массовую память. EMPTY-BUFFERS следует использовать, когда
в массовую память может быть записана неверная информация. EMPTY-BUFFERS не нужно в
стандарте Форт-83, так как стандарт запрещает помещать в буфер что-либо, что нельзя записать.
(Но это требование часто игнорируется.) См. также BLOCK; SAVE-BUFFERS; FLUSH.
END-CODE "end-code" 83ASM 79ASM
( sys --)
Завершает описание слова в Форт-ассемблере. При использовании в форме
CODE <name> ... END-CODE
255
или
: <namex> ... <create> ... ;CODE ... END-CODE
завершает описание, начатое оператором CODE или словом-описателем, которое использует ;CODE
и позволяет находить слова <name> или <namex> в словаре. Форт-79 требует также, чтобы оператор
END-CODE преобразовал контекстный словарь в текущий. Используется для предотвращения на¬
хождения в словаре или исполнения слова, описанного с ошибкой. Некоторые версии допускают, но
не требуют END-CODE (например, MMSFORTH).
ERASE ’’erase” ”стереть" 83CNT 79RES MMS
( адр u —)
Обнуляет u байтов в памяти, начиная с адреса ”адр”. Если u равно 0, ничего не производится.
EXECUTE ’’execute” "исполнить” 83REQ 79REQ
( адр ~)
Исполняет слово, адрес поля программы ”адр” которого лежит в стеке. Хотя Форт-83 требует, что¬
бы выдавалось сообщение об ошибке, если ”адр” не является адресом поля программы, это очень
трудно реализовать, и поэтому такое требование вообще игнорируется. Большинство версий разру¬
шаются, если попытаться выполнить EXECUTE для произвольного адреса.
EXIT ”exit” "уход" 83REQ 79REQ
( ~>
Если встречается внутри слова-двоеточия, заставляет Форт ”уйти” из этого слова и вернуться к ис¬
полнению слова, от которого произошло обращение. В большинстве версий EXIT может использо¬
ваться, чтобы досрочно завершить интерпретацию блока, возвратив управление клавиатуре. EXIT
не может использоваться внутри цикла do-loop. Одной из функций ; является компиляция EXIT
(или заменяющего слова) для завершения описания типа двоеточие.
EXPECT ’’expect” 83REQ 79REQ
( адр n —)
Прерывает исполнение программы, чтобы принять серию символов с клавиатуры и записать их в
память. Символы запоминаются, начиная с адреса ”адр”, а запоминание продолжается в направле¬
нии старших адресов до тех пор, пока не будет введен символ ’’возврат каретки” (ASCII 13) или
пока не поступит n символов. Все введенные и записанные в память символы отображаются на эк¬
ране по мере их ввода. Код ’’возврат каретки” не будет записан в память. Ни одного символа не бу¬
дет занесено в память, если n равно 0. Форт-79 требует также, чтобы в конце введенного текста по
завершении EXPECT было записано два нуля. Форт-83 требует, чтобы возврат каретки был отобра¬
жен как пробел. См. также SPAN.
FENCE ’’fense” "ограда” MVP FIG VAR
( -- адр)
Переменная, которая определяет адрес в словаре, ниже которого нельзя производить стирание сло¬
варя с помощью FORGET.
FILL ”fiil” ”запомнить" 83REQ 79REQ
( адр nl n2 --)
Заполняет nl байтов памяти, начиная с адреса адр, кодом, содержащимся в младшем байте числа
n2. Если *nl меньше или равно 0, ничего не делается. В Форт-83 nl предполагается числом без зна¬
ка и заполнение блокируется, если оно равно 0.
FIND ”find” "найти" 83REQ 79REQ
FIND в Форт-83 и Форт-79 выполняют практически совершенно разные функции. Схема трансфор¬
мации стека в Форт-83 выглядит как
( адр1 — адр2 n)
где ”адр1” является адресом счетной строки, содержащей имя <name> слова, которое должно быть
найдено в словаре. Если <name> не найдено, ”адр2” равен ”адр1”, а n=0. Если <name> найдено,
”адр2” равен адресу поля программы <name>, а ”п” равно 1, если <name> — слово немедленного
исполнения, в противном случае значение ”п” приравнивается минус единице. В Форт-79 схема
преобразования стека имеет вид
( — адр) или ( -- 0)
где FIND используется в форме
FIND <name>
и заносит в стек либо адрес поля программы <name>, либо нуль, если <name> в словаре не найдено.
FL/ ”f-I-divide” TXT
( nl n2 -- n3)
256
Используя операторы Форт-79, делит число nl на n2 и засылает в стек округленное по нижней гра¬
нице значение частного n3. Моделирует (медленно) оператор / Форт-83, используя Форт-79.
: FL/ /MOD SWAP IF DUP 0 < IF 1- THEN THEN ;
FL/MOD ”f-l-divide-mod” TXT
( nl n2 — n3 n4)
Используя операторы Форт-79, осуществляет деление с нижней границей nl на n2 и засылает в
стек остаток n3 и округленное значение частного n4. Моделирует (медленно) оператор Форт-83
/MOD, используя Форт-79.
: FL/MOD 2DUP FLMOD ROT ROT FL/ ;
FLMOD ”f-l-mod” TXT
( nl n2 -- n3)
Используя операторы Форт-79, выполняет деление с нижней границей nl на n2 и засылает в стек
остаток n3. Моделирует (медленно) оператор Форт-83 MOD, используя Форт-79.
: FLMOD 2DUP FL/ * — ;
FLUSH ’’flush” 83REQ MMS VAR
( ~)
Копирует содержимое всех блочных буферов, помеченных для спасения, в соответствующие блоки
массовой памяти. Синоним — SAVE-BUFFERS в большинстве версий Форта. В Форт-83 требуется,
чтобы приписка буферов к блокам была ликвидирована, в то время как SAVE-BUFFERS этого не
требует.
FORGET ’’forget” "забыть" 83REQ 79REQ
( -)
При использовании в форме
FORGET <name>
уничтожает слово <name> и все слова, добавленные в ёловарь после <name>, вне зависимости от их
принадлежности к контекстным словарям, если <name> найдено в контекстном словаре, куда вводи¬
лись описания. (В Форт-79 — это контекстный словарь CURRENT.) Если <name> не найдено, дает¬
ся сообщение об ошибке.
FORTH ’’forth” 83REQ 79REQ
( ~)
Имя первичного контекстного словаря. Исполнение слова FORTH делает словарь FORTH первым и
единственным, который просматривается при поиске слов. Новые описания становятся частью сло¬
варя FORTH до тех пор, пока с помощью DEFINITIONS не будет сделан контекстным другой сло¬
варь. См. также CURRENT; CONTEXT; DEFINITIONS; VOCABULARY.
FORTH-83 ”forth-83” 83REQ
( ~)
Сообщает, соответствует ли система стандарту Форт-83. Если стандартная система Форт-83 не ис¬
пользуется, слово не будет найдено, а будет дано сообщение об ошибке.
HERE ”here” "здесь" 83REQ 79REQ
( -- адр)
Засылает в стек адрес очередной свободной позиции в словаре, указатель словаря.
HEX ”hex” 83CNT 79RES TXT MMS
Выбирает для ввода-вывода шестнадцатеричную систему счисления.
: НЕХ 16 BASE ! ;
HI# ”high-number” MMS
( - n)
MMSFORTH воспринимает все числовые вводы как числа двойной длины, помещая число двойной
длины в стек, если входная последовательность содержит десятичную точку, и посылая в стек число
одинарной длины в противном случае. В обоих вариантах QUAN HI# содержит старшие 16 битов
последнего из введенных чисел.
HOLD ”hold” 83REQ 79REQ
( n ~)
Вводит символ с ASCII-кодом n в форматированную цифровую выходную строку. Используется
между <# и #>. См., например, #.
I "i" 83REQ 79REQ
( -- n) или ( -- u)
Заносит в стек индекс цикла do-loop. Может использоваться только непосредственно, а не в словах,
к которым происходит обращение в цикле do-loop. См. также Г; J; К.
9 М. Келли 257
r "i-prime" 83UNC 79REQ MMS
( — n) или ( — u)
Засылает в стек индекс цикла при использовании в слове типа двоеточие, к которому происходит
обращение в цикле do-loop. См. также I; J\
IF "if" "если” I. С 83REQ 79REQ
( флаг --)
Открывает одно- или двухветвевую структуру. Используется в форме
: <name> ... флаг IF ... THEN ... ;
ИЛИ
: <name> ... флаг IF ... ELSE ... THEN ... ;
Когда <name> исполняется, оператор IF предполагает наличие в стеке флага. Если флаг не равен 0,
исполняются слова между IF и ELSE (или слова между IF и THEN, если ELSE не используется).
Но если значение флага равно 0, управление передается словам между ELSE и THEN (или слову
после THEN, если ELSE отсутствует). В обоих случаях исполнение продолжается после THEN.
Структуры IF...ELSE...THEN допускают вложения.
IMMEDIATE "immediate” ”немедленное” 83REQ 79REQ
( ~)
Помечает только что созданное слово словаря так, что оно будет*исполнено (а не скомпилировано)
даже если Форт находится в режиме компиляции.
IN$ - ”in-string” ”ввод строки” MMS
( -- адр)
Отображает запрос ”?” и ожидает ввода с клавиатуры строки, завершающейся кодом ’’возврат ка¬
ретки”. Счетная строка засылается в буфер PAD, адрес которого ”адр” кладется в стек. См. также
$IN.
INDEX ”index” ”индекс” 83UNC 79RES MMS
( nl n2 --)
Отображает верхние строки каждого из блоков в диапазоне от nl до n2. n2 — в большинстве версий
Форта верхняя граница диапазона, а в MMSFORTH — число блоков.
INKEY$ ”in-key-string” MMS
( - адр)
Создает в PAD односимвольную счетную строку, содержащую код символа нажатой клавиши, в
стек засылается адрес PAD в виде ”адр”. Если не нажато никакой клавиши, то при исполнении
INKEY$ строка в PAD не будет содержать ничего (байт-счетчик равен 0). INKEY$ не ждет ввода.
INSTR ”in-s-t-r” MMS
( адр1 адр2 — n)
Производит поиск строки, хранящейся по адресу ”адр2” в строке, начинающейся с адреса ”адр1”.
Если поиск успешен, в стек заносится начальная позиция этой строки по отношению ”адр1”, в про¬
тивном случае — нуль.
IS ”is” MMS
( n ~)
Записывает число в слово типа QUAN. При использовании в форме:
n IS <name>
записывает n в слово типа QUAN с именем <name>. n — число с длиной, соответствующей
CQUAN, QUAN, 2QUAN или 4QUAN. Противостоит АТ.
J "j" 83REQ 79REQ
( - n)
Используется во вложенных циклах do-loop в форме
DO ... DO ... J ... LOOP ... +LOOP ;
Засылает в стек значение индекса очередного внешнего цикла, в данном примере цикла, завершаю¬
щегося оператором +LOOP. Подобно I, J может использоваться только непосредственно, а не внутри
другого слова, к которому происходит обращение внутри цикла.
J* ”j-prime” TXT
( - n)
Выполняет функцию J, но внутри слова типа двоеточие, к которому происходит обращение внутри
цикла. Аналог Г.
: J’ R> R> R> R> R@ SWAP >R SWAP >R SWAP >R SWAP >R ;
258
K ”k” 83CNT 79RSV
( - n)
Используется во вложенных циклах do-loop в форме
DO...DO...DO...K...+LOOP...+LOOP...LOOP
Засылает в стек значение индекса цикла второго по отношению к тому, где применено К, в данном
случае цикла, завершаемого оператором LOOP. См. также I, J.
KEY ”кеу” ”клавиша” 83REQ 79REQ
( - n)
Приостанавливает исполнение программы и ждет нажатия клавиши, после чего помещает в стек ее
ASCII-код. Символ, полученный KEY на экране не отображается [Форт-83 требует, чтобы вводи¬
мый код был не более 127 (7 бит), но фактически все версии игнорируют это требование и допуска¬
ют прием восьми битов (байта), т. e. кодов до 255.]
L "list" MMS VAR
( ~)
Отображает блок, номер которого содержит переменная SCR, обычно это блок, который только что
отображался или редактировался.
: L SCR @ UST ;
L>NAME ”link-to-name” 83FLD
( адр1 - адр2)
Исходный код ”адр1” — адрес поля связи слова, засланный в стек код ”адр2” — адрес поля имени
этого слова.
LABEL ’’label” "метка” TXT MMS
( -)
Слово-описатель, которое позволяет создавать именованные машинные программы, написанные на
Форт-ассемблере. При исполнении в форме
LABEL <name>
формируется в словаре статья с именем <name> и в качестве контекстного словаря выбирается
ASSEMBLER, с которого и начинается поиск. Это позволяет описать <name> с применением мнемо¬
ники ассемблера. Когда <name> исполняется, в стек заносится адрес его поля параметров, пригод¬
ный для использования словом ассемблера CALL.
: LABEL CREATE [COMPILE] ASSEMBLER ;
Подробности — на стр.231.
LEAVE ’’leave” ”уйти” 83REQ 79REQ
( -)
Вызывает немедленное прерывание цикла do-loop. Исполняетсяьзовании в форме
: <name> ... DO ... LEAVE ... LOOP ... ;
Когда LEAVE встречается в тексте программы, цикл do-loop прерывается. Это делается обычно при
выполнении определенного условия в структуре IF...THEN. При прерывании цикла в Форт-83 слова
между LEAVE и LOOP или +LOOP не исполняются, в то время как в Форт-79 они выполняются. В
обоих случаях исполнение продолжается со слова после LOOP.
LEFT$ ’’left-string” MMS
( адр1 n — адр2)
Извлекает n символов из начала счетной строки с адресом ”адр1” и создает новую счетную строку
в PAD. В стек засылается адрес PAD (адр2).
LEN ”len” MMS
( адр — n)
Засылает в стек длину счетной строки с адресом ”адр”. Эквивалент C@.
LFA ”i-f-a” FlG VAR
( адр1 - адр2)
Исходный код ”адр1” — адрес поля параметров слова, в стек заносится ”адр2” — адрес поля связи
этого слова.
LINK> ”from-link” 83FLD
( адр1 - адр2)
Исходный код ”адр1” — поле связи слова, в стек заносится ”адр2” — адрес поля программы этого
слова.
LIST ”list” 83CNT 79REQ
( u —)
Отображает содержимое блока u. Переменной SCR присваивается значение u.
LISTS "lists" MMS
( nl n2 ~)
Отображает n2 блоков, начиная с блока nl, каждый из блоков представляется в формате LIST.
LIT "lit" FIG VAR
( - n)
Слово, компилируемое LITERAL для того, чтобы заносить в стек следующее за ним в словаре 16-
битовое число. Подробности на стр. 209. См. также COMPILE.
LITERAL "literal" "литерал” I, С 83REQ 79REQ
( n --)
Обычно используется в форме
: <name> ... [n] LITERAL ... ;
Компилирует LIT (или его эквивалент), а вслед за ним — число (n), которое в момент компиляции
лежит в стеке. Когда <name> исполняется, n заносится в стек. Подробности на стр. 207.
LL "list-last" TXT MVP
( ~>
Отображает блок, предшествующий по номеру блоку, который только что отображался или редак¬
тировался.
LN "list-next" TXT MVP
( ~>
Отображает блок, следующий по номеру за блоком, который только что отображался или редакти¬
ровался.
LOAD "ioad" "загрузить" 83REQ 79REQ
( u —)
Начинает интерпретацию блока ”и”, сделав его ’’входным потоком”. Значения >IN и BLK сохраня¬
ются, после чего производится интерпретация блока. Если интерпретация в явной форме не преры¬
вается, она будет прекращена, когда входной поток иссякнет. Когда загрузка выполнена, интерпре¬
тация продолжается с места, откуда было выполнено обращение ( т.е. восстанавливаются прежние
значения переменных >IN и BLK).
LOADS "loads" TXT MMS
( nl n2 ~)
Загружает n2 блоков, начиная с блока nl.
: LOADS OVER + SWAP DO I LOAD LOOP ;
LOOP "loop" "цикл” I, С 83REQ 79REQ
( ~)
При использовании в форме
: <name> ... DO ... LOOP ... ;
определяет конец цикла do-loop. Когда <name> исполняется, DO устанавливает начальное значение
индекса и предела цикла. По завершении очередного цикла LOOP дает единичное приращение ин¬
дексу цикла. В Форт-83, если в результате приращения индекс перешел границу между пределом
минус 1 и пределом, цикл завершается. В Форт-79 цикл завершается не при переходе через эту
границу, а при индексе, большем или равном пределу. Если цикл не прерван, управление передает¬
ся слову, следующему за DO. Смотри обсуждение прерывания циклов в гл. 8. См. также +LOOP;
DO.
М* "m-times" "М-умножить" MMS
( nl n2 - d)
Перемножает два числа одинарной длины nl и n2, засылая в стек произведение двойной длины d.
Используется, если возможно переполнение при *.
M*/ "m-times-divide” "М-умножить-разделить" MMS
( dl nl n2 - d2)
Умножает содержимое dl на nl и делит произведение на n2. В стек засылается частное d2. Резуль¬
тат умножения dl на nl для предотвращения арифметического переполнения величина dl*nl имеет
промежуточный 48-битовый формат (тройная длина).
M+ "m-plus" "М-плюс" MMS
( dl n - d2)
Складывает значения содержимого n и dl, засылает в стек сумму двойной длины d2.
М- "m-minus" "М-минус" MMS
260
( d nl - d2)
Вычитает содержимое n из dl, засылает в стек разность двойной длины.
М/ "m-^ivide” "М-разделить" MMS
( d nl -- n2)
Делит d на nl, засылает в стек частное одинарной длины n2 (округление в сторону нуля).
M/MOD "m-divide-mod” "М-разделить с остатком" MMS
( d nl — n2 n3)
Делит d на nl, засылает в стек остаток n2 одинарной длины и частное одинарной длины n3 (округ¬
ление в сторону нуля).
МАХ ”тах” "макс” 83REQ 79REQ
( nl n2 — n3)
Засылает в стек число n3, которое является большим из nl и n2. См. также DMAX; MIN.
MID$ ”mid-string” "MID-ШСГ ” MMS
( адр1 nl n2 — адр2)
Извлекает счетную подстроку из счетной строки с адресом адр1, начиная с адреса адр1+п1, копиру¬
ет n2 символа в новую строку и укладывает ее в PAD, адрес которого адр2 заносится в стек.
MIN ”min” "мин" 83REQ 79REQ
( nl n2 — n3)
Засылает в стек число n3, которое является меньшим из nl и n2. См. также DMIN; МАХ.
MOD ”mod” "остаток" 83UNC 79REQ
( nl n2 — n3)
Делит nl на n2 и засылает в стек остаток n3. В Форт-83 используется деление с нижней границей,
в то время как в Форт-79 частное округляется в направлении нуля. В Форт-83 при делителе, рав¬
ном 0, или при частном вне диапазона -32.768 — 32.767 дается сообщение об ошибке.
MOVE ’’move” "перенести" 83UNC 79REQ
( адр1 адр2 n —)
Переносит n чисел одинарной длины, начиная с адреса адр1 в память, начиная с адреса ”адр2”. Пе¬
ренос происходит от младших адресов в направлении старших. Форт-79 предписывает, что n — чис¬
ло одинарной длины со знаком, если n меньше или равно 0, ничего не переносится. В Форт-83 n —
число без знака и, если n равно 0, перенос не производится. См. также CMOVE; <CMOVE;
CMOVE>.
MYSELF ’’myself” MMS VAR
( -)
Позволяет слову обращаться к самому себе. При использовании в форме
: <narae> ... MYSELF ... ;
в поле параметров <name> компилируется адрес поля программы <name>, причем место в поле па¬
раметров задается положением слова MYSELF в описании. Чтобы обеспечить уход из цикла,
MYSELF обычно вводится внутрь условных структур, в противном случае цикл будет бесконечным.
Синоним — RECURSE. Обсуждение рекурсии в Форте смотри на стр. 221.
N>LINK ”name-to-link” 83FLD
( адр1 - адр2)
Исходный код адр1 — адрес поля имени слова, в стек засылается адр2 — адрес поля связи этого
слова.
NAME> ”from-name” 83FLD
( адр1 - адр2)
Исходный код адр1 — адрес поля имени слова, в стек заносится адр2 — адрес поля программы это¬
го слова.
NCASE ”n-case” MMS
( n —)
Открывает цифровую CASE-структуру. Формат использования: NCASE nl n2 n3 ” <namel> <name2>
<name3> OTHERWISE...CASEND
Список чисел (здесь nl, n2 и n3) завершается пробелом и двойной кавычкой, используется для вы¬
бора и исполнения слова из списка слов, следующего за ”. Используются только 8 младших битов
каждого числа. Таким образом, если в стеке nl, управление будет передано <namel>, аналогично,
если в стеке n2, управление передается <name2>. Когда выбранное слово исполнено, управление пе¬
редается слову, следующему за CASEND. Если число в стеке не соответствует ни одному из чисел в
списке, исполнение продолжится со слова после OTHERWISE (если оно присутствует) или после
CASEND. См. также ACASE.
261
NEGATE ’’negate” ”сменить знак” 83REQ 79REQ
( nl - n2)
Реверсирует знак числа nl и заносит в стек результат в виде n2. n2 — дополнение nl по модулю
два (т.е. нуль минус nl).
NEXT ”next” ”следующий" MMS VAR
( -)
Слово MMSFORTH-ассемблера, которое компилирует оператор перехода ко внутреннему интерпре¬
татору, таким образом завершая описание слова ассемблера. Другие версии могут использовать сло¬
ва с другим именем для реализации этой функции. Подробности на cmp. 215.
NFA ”n-f-a” FIG VAR
( адр1 — адр2)
Исходный код адр1 — адрес поля параметров слова, в стек заносится адр2 — адрес поля имени это¬
го слова.
NIP ”nip”
( nl n2 — n2)
Ликвидирует второе сверху число в стеке. Эквивалентно
: NIP SWAP DROP ;
NOT ”not” 83REQ 79REQ
NOT описано совершенно по разному в Форт-83 и в Форт-79. Схема преобразования стека в Форт-
83 имеет вид
( n 1 - n2)
где n2 — дополнение nl по модулю один. To есть все биты n2 реверсированы по отношению к nl.
Схема преобразования стека в Форт-79 имеет вид
( флаг1 — флаг2)
где флаг1 имеет обратное значение истинности по отношению к флаг2. Описание в Форт-79 эквива¬
лентно 0=, т.е. флаг истинно (не 0) будет преобразован в нуль, а флаг ложно (0) — во флаг ис¬
тинно (1).
NUMBER ’’number” 83UNC 79RES MMS
Обычно ( адр -- d) MMSFORTH (адр1 — n адр2) или (адр1 -d адр2) преобразует счетную строку с
адресом адр в 32-разрядное целое число со знаком, при этом учитывается значение — BASE, ре¬
зультат заносится в стек. Если цифровое преобразование не возможно, дается сообщение об ошиб¬
ке. Строка может содержать в начале знак минус. NUMBER в MMSFORTH засылает в стек число
одинарной или двойной длины в зависимости от того, содержит ли строка код десятичной точки.
Переменным HI# и #PT присваиваются соответствующие значения. адр2 — адрес первого непре-
образуемого символа.
OCTAL ’’octal” 83CNT 79RES MMS
( -)
Выбирает восьмеричную систему счисления для ввода-вывода.
: OCTAL 8 BASE ! ;
OR ”or” "ИЛИ" 83REQ 79REQ
( nl n2 - n3)
Выполняет побитовую операцию ИЛИ над числами nl и n2 и засылает результат в виде n3. Таким
образом, каждый бит числа nl сравнивается с соответствующим битом n2, и если один из них или
оба равны 1, то соответствующий бит n3 будет равен 1; в противном случае этот бит будет равен 0.
В двоичной форме
110 100 OR
занесет в стек двоичное 110. См. также AND; XOR.
OTHERWISE ’’otherwise” "в противном случае" MMS
( -)
Может использоваться в структурах ACASE или NCASE для того, чтобы пометить место продолже¬
ния, исполнения программы, если условие не выполнено. Смотри, например, ACASE и NCASE.
(Заметьте, что это слово в MMSFORTH полностью не совпадает со словом Форт-83 из неконтроли¬
руемого списка и словом Форт-79 из контролируемого списка.)
OVER ”over” "через” 83REQ 79REQ
( nl n2 — nl n2 n3)
Копирует второе сверху число в стеке на верх стека.
262
PAD "pad” 83REQ 79REQ
( -)
Заносит в стек самый ’’нижний” адрес буфера, который может быть использован для временного
хранения информации . Адрес PAD изменяется, а данные, записанные там, теряются, если изменя¬
ется адрес следующей свободной ячейки словаря (засылаемый в стек HERE), т.е. если что-то будет
добавлено в словарь. Минимальная емкость PAD задается в Форт-83 равной 84 символам и в Форт-
79 — 64 символам.
PAGE ”page” "страница” 83UNC 79RES MMS
( -)
Очищает экран терминала и помещает курсор в верхний левый угол. При выводе на печать выдает¬
ся код ’’перевод страницы”.
PCRT ”p-c-r-t” MMS
( ~)
Обеспечивает вывод на печать и экран (ЭЛТ) одновременно.
PEMIT ”p-emit” MVP
Аналог EMIT, но посылает символ не на видеотерминал, а на печатающее устройство.
PFA ”p-f-a” FIG VAR
( адр1 - адр2)
Исходный код адр1 — адрес поля имени слова, в стек заносится адр2 — адрес поля параметров это¬
го слова.
PICK ”pick” 83REQ 79REQ
( nl -- n2)
Копирует nl-й код в стеке и заносит его на верх стека. Само число nl при этом не считается. В
Форт-83 верхний элемент стека имеет номер нуль, в то время как в Форт-79 — один. Таким обра¬
зом, 0 PICK эквивалентно DUP в Форт-83 и 1 PICK в Форт-79. В большинстве версий PICK —
очень медленная команда по сравнению с другими операторами стека.
PLIST "p-iists” MMS
( nl n2 —)
Отображает n2 блоков, начиная с nl. Формат выдачи приспособлен для печати, строки пронумеро¬
ваны, а блоки группируются по три на странице.
PP ”р-р” TXT FIG MVP VAR
( nl n2 -)
При использовании в форме
nl n2 PP <текст>
замещает строку n2 блока nl <текстом>, следующим за PP. <текст> завершается кодом <enter>.
Описание представлено на стр.310.
PRINT "print” "печапшть" MMS
( ~)
Переадресует вывод на печатающее устройство.
PSH "push” TXT MMS
(-)
Слово ассемблера MMSFORTH, которое компилирует машинную программу, засылающую содержи¬
мое одного машинозависимого двойного регистра в стек с последующей передачей управления внут¬
реннему интерпретатору. Используется для завершения описания CODE-слов. Выполняет функцию
NEXT. См. также PSH2.
PSH2 ”push-two” TXT MMS
( --)
Слово ассемблера MMSFORTH, которое компилирует машинную программу, засылающую содержи¬
мое двух машинозависимых двойных регистров в стек с последующей передачей управления
внутреннему интерпретатору. Используется для завершения описания CODE-слов. Выполняет фун¬
кцию NEXT. См. также PSH.
PSH3 ”push-two” тхт
(--)
Слово ассемблера MMSFORTH, которое компилирует машинную программу, засылающую содержи¬
мое трех машинозависимых двойных регистров в стек с последующей передачей управления внут¬
реннему интерпретатору. Используется для завершения описания CODE-слов. Выполняет функцию
NEXT. См. также PSH. Описание смотри на стр. 317.
PTC "p-t-c” MMS
263
( nl n2 --)
Помещает курсор в строку nl и столбец n2 экрана дисплея.
QUAN "quan" MMS
Слово-описатель QUAN-типа — для чисел одинарной длины. При исполнении в форме
QUAN <narae>
формирует в словаре статью с именем <name> и резервирует место для числа одинарной длины.
При использовании самого <name> число, которое оно содержит, заносится в стек. Когда перед ним
стоит IS, число из стека заносится в поле параметров <name>. Если перед ним стоит АТ, в стек за¬
носится адрес поля параметров <name>. (Обсуждение QUAN смотри на стр. 100). См. также
CQUAN; 2QUAN; 4QUAN.
QUERY ’’query” 83CNT 79REQ
( ~)
Приостанавливает работу программы на время приема символов с клавиатуры и записи их в тексто¬
вый входной буфер. Передача завершается, когда внесен код ’’возврат каретки” или заполнен весь
входной буфер (стандарт требует, чтобы буфер вмещал не менее 80 символов). Форт-83 требует
сброса величин >IN и BLK в нул* и приравнивая значения #TIB значению SPAN. Для приема тек¬
ста из буфера может использоваться оператор WORD. См. также >IN; #TIB; BLK; EXPECT;
SPAN; WORD. Примеры и обсуждение на стр. 115.
QUIT ”quit” 83REQ 79REQ
( ~)
Вызывает немедленный уход из программы и передачу управления оператору. Очищает стек воз¬
вратов и подготавливает систему для интерпретации данных, поступающих с активного устройства
(обычно клавиатуры). Стек параметров не очищается (в отличие от ABORT), и никаких сообщений
или запросов на экран не выдается (даже ”ок”). См. также ABORT.
R/ ’’round-divide” ”деление с округлением” TXT
( nl n2 — n3)
Делит nl на n2 и засылает в стек частное n3, которое округлено в большую сторону, если остаток
больше n2/2.
R> ”r-from” "из-R” 83REQ 79REQ
( - n)
Удаляет n из стека возвратов и помещает его в стек параметров. R> вообще должен использоваться
в паре с >R, чтобы исключить изменение указателя стека возвратов. См. также R@; >R.
R@ "r-fetch” ”занести R” 83REQ 79REQ
( - n)
Копирует n из стека возвратов в стек параметров (без изменения стека возвратов). R@ полезно,
когда стек возвратов используется для временного хранения чисел внутри описаний типа двоеточие.
См. также >R; R>.
RBLK "r-block” "R-блок” MMS
( адр n --)
Читает блок n и укладывает его в память, начиная с адреса адр. См. также WBLK.
REQURSE ’’recurse” ”рекурсия” 83CNT
( ~)
Синоним MYSELF.
REPEAT ’’repead” ”повторить” I, С 83REQ 79REQ
( -)
Завершает цикл BEGIN...WHILE...REPEAT, При использовании в форме:
: <narae> ... BEGIN ... флаг WHILE ... REPEAT ... ;
компилирует безусловный переход на BEGIN в описании слова <name>. Когда <name> исполняется,
слова между BEGIN и REPEAT повторно исполняются до тех пор, пока флаг в стеке остается не
равным 0. Если флаг соответствует ложно (0), управление передается слову, следующему после
REPEAT. См. также BEGIN; WHILE; UNTIL.
RIGHT$ "right-string” ”правая строка” MMS
( адр1 n — адр2)
Берет n символов с правого конца счетной строки, лежащей по адресу адр, формирует новую счет¬
ную строку в PAD, адрес которого заносит в стек.
ROLL ”го1Г 83REQ 79REQ
( ... n -)
264
Удаляет n-й элемент из стека (не считая n) и заносит его на верх стека, смежные числа смещаются
вниз на одну позицию. В Форт-83 верхний элемент имеет номер 0, так что,например, 2 ROLL эк¬
вивалентно ROT; его эквивалентом в Форт-79 является 3 ROLL, так как здесь верхняя позиция
стека имеет номер 1.
ROT "rote” 83REQ 79REQ
( nl n2 n3 — n2 n3 nl)
Переносит третий сверху элемент на верх стека.
RUP/ "round-up-divide” ”деление с округлением в большую сторону” TXT
( nl n2 — n3)
Делит nl на n2 и заносит в стек частное n3, округленное в большую сторону, если остаток не равен
0. Описание в Форт-79:
: RUP/ /MOD SWAP О- О- + ;
Описание в Форт-83:
: RUP/ /MOD SWAP О- О- NEGATE + ;
S-D ”single-to-double” ’’одинарное в двойное” TXT
( n - d)
Преобразует число одинарной длины в число двойной длины, сохранив правильный знак. Описание
в Форт-79:
: S-D DUP 0< NEGATE ;
Описание в Форт-83:
: S-D DUP 0< ;
SAVE-BUFFERS ”save-buffers” ”сохранить буферы” 83REQ 79REQ
( -)
Копирует содержимое всех блочных буферов, помеченных оператором UPDATE, в соответствующие
блоки массовой памяти. Все буферы помечаются так, как если бы они не были модифицированы, но
Форт-83 допускает сохранение приписки к определенным буферам, что зависит, конечноу от версии.
См. также FLUSH.
SCR ”S-C-r” 83CNT 79REQ
( - адр)
Переменная, содержащая номер блока, которой был только что отображен (и во многих версиях
блок, который только что редактировался).
SIGN "sign” "знак” 83REQ 79REQ
( n —)
Добавляет ASCII-код ”-” (знак минус) в начало отформатированной числовой выходной строки, ес¬
ли n отрицательно. Использование между <# и #> не является обязательным (но типичным) в
Форт-83. Форт-79 требует этого в обязательном порядке. См., например, #.
SMUDGE "smudge” FIG MVP VAR
( -)
Меняет состояние бита (бита-метки) в заголовке только что описанного слова так, чтобы разрешить
или нет нахождение этого слова при просмотре словаря. Если слово может быть найдено, SMUDGE
пометит его так, что его уже нельзя будет найти. Повторное выполнение SMUDGE сделает его "на¬
ходимым” снова. В процессе описания слова бит-метка устанавливается так, что слово нельзя най¬
ти, исполнение SMUDGE делает его находимым. Это еще один из нескольких методов предотвра¬
щения исполнения описаний, содержащих ошибки.
SP@ ”s-p-fetch” "занести SP” 83CNT 79RES MMS VAR
( - адр)
Заносит в стек адрес верхнего элемента стека параметров до исполнения слова SP@.
SPACE "space” "пробел" 83REQ 79REQ
( ~)
Посылает код пробела (ASCII 32) на активное в данный момент выходное устройство.
SPACES ’’spaces” "пробелы" 83REQ 79REQ
( n —)
Посылает n кодов пробела на активное в данный момент выходное устройство. Если n равно 0 или
отрицательно, ничего не отображается.
SPAN "span” 83REQ
265
( -- адр)
Переменная, которая содержит число символов, введенных и запомненных при последнем исполне¬
нии EXPECT. Подробности на cmp. 117.
SSIGN ”s-sign” TXT
( n dl - d2)
Помещает знак n в форматированную числовую выходную строку, предназначенную для отображе¬
ния числа n. Должно использоваться с <S#. См. также SIGN и обсуждение на стр. 71.
: SSIGN ROT SIGN ;
STATE "state” Vсостояние" 83REQ 79REQ
( -- адр)
Переменная, которая содержит число, определяющее состояние компиляции. Ненулевое значение
указывает, что происходит компиляция (действительное значение Зависит от конкретной системы),
а нуль указывает, что осуществляется интерпретация. В стандарте Форт-83 программе запрещено
менять значение слова STATE.
STR$ "s-t-r-string” MMS
( n — адр)
Преобразует n в счетную строку, хранящуюся в PAD, преобразование осуществляется с учетом зна¬
чения BASE, в стек заносится адрес ”адр” слова PAD.
STRING$ "string-string” MMS
( nl n2 — адр)
Помещает счетную строку, состоящую из nl байтов символов, ASCII-код которых равен n2 в PAD,
в стек заносится адрес PAD ”адр”.
SWAP "swap” ”поменять местами” 83REQ 79REQ
( nl n2 — n2 nl)
Меняет местами два верхних числа в стеке.
TASK ”task” "задание" TXT
( ~)
TASK обычно описывается как
: TASK ;
в начале программы, так что команда
FORGET TASK
удалит из.стека словаря программу, загруженную последней. Добавление короткого описания типа:
: TASK .” Имя программы ” ;
позволит слову TASK идентифицировать последнюю загруженную программу.
THEN ”then” "затем" I, С 83REQ 79REQ
( -)
Отмечает конец условной ветви программы. При использовании в форме
: <name> ... флаг IF ... THEN ... ;
ИЛИ
: <name> ... флаг IF ... ELSE ... THEN ... ;
отмечает место, откуда продолжится исполнение по отношению к соответствующим IF или ELSE.
Когда IF конструкция завершена, исполнение продолжается с оператора, стоящего за THEN.
THRU "through” 83CNT 79RES MVP VAR
( nl n2 —)
Последовательно загружает блоки от nl до n2. См. также LOADS.
TIB ”t-i-b” 83REQ MMS VAR
( - адр)
Заносит в стек адрес начала входного текстового буфера (иногда называемого ’’терминальным вход¬
ным буфером”). Этот буфер используется для приема символов, поступающих с клавиатуры.
TL ”t-l” MMS
( nl n2 ~)
Отображает строки от nl до n2 (с номерами строк) блока, номер которых лежит в SCR. Таким об¬
разом, команда
0 15 TL
отобразит весь блок, номер которого записан SCR.
TOKEN ’’token” "лексема" MMS
( n адр1 — адр2)
266
Переносит строку символов (лексему), лежащую по адресу ”адрГ\ и укладывает в виде счетной
строки, начиная с адреса, указанного словом HERE, игнорируя предшествующие пробелы и исполь¬
зуя символ с ASCII-кодом n в качестве разграничителя. Копия разграничителя записывается после
выделенной лексемы, но не учитывается в байт-счетчике строки. адр2 равен адресу символа, следу¬
ющего за разграничителем в блочном буфере, или 0, если лексема не выделена. Слово TOKEN ис¬
пользуется в описаййи слова WORD в MMSFORTH и полезно для выделения строк из массивов
данных.
TRAVERSE ’’traverse” ”траверс” FIG MVP VAR
( адр1 n — адр2)
Находит адрес противоположного края поля имени слова в словаре при начальном адресе адр1. Ес¬
ли n равен 1, поиск производится в направлении больших адресов, при n, равном 0, — в направле¬
нии меньших. Во многих версиях Форта первый и последний байты поля имени откомпилированно¬
го слова имеют старший бит, установленный равным 1 (т.е. имеют значение больше 80 шестнадца¬
теричного). Это позволяет TRAVERSE найти начало или конец поля имени бесконечной длины, вы¬
числить другие адреса в откомпилированном описании. Используется в NFA и PFA.
TUCK ”tuck” TXT F83
( nl n2 — n2 nl n2)
Копирует верхний элемент n2 и ’’подсовывает” его под второй сверху элемент nl. Может быть опи¬
сано как *
: TUCK SWAP OVER ;
но в версии F83 слово TUCK описано на ассемблере с целью ускорения его работы.
TYPE ”type” *1опюбразить” 83REQ 79REQ
( адр n --)
Отображает строку из n символов, хранящуюся в памяти по адресу адр. Если n равно 0 или меньше
0, ничего не отображается. См, также COUNT.
U* ”u-times” ”U-умножить** 79REQ
( ul u2 — ud)
Умножает ul на u2 и засылает в стек произведение двойной длины ud. Все коды рассматриваются
как числа без знака. Синоним слова UM*, используемого в Форт-83.
U. ”u-dot” "U-точка” 83REQ 79REQ
( u —)
Отображает число без знака u в позиции, отмеченной курсором. После числа вводится пробел. См,
также . .
U.BIN ”u-dot-binary” TXT
( u - u)
Отображает число без знака, лежащее в стеке в двоичном представлении, не изменяя состояния
стека или величины BASE.
: U.BIN DUP BASE @ 2 BASE ! SWAP U. BASE ! ;
U.R ”u-dot-r” 83CNT 79RES MMS
( u n —)
Отображает величину числа без знака u с учетом величины BASE в поле шириной n позиций.
Младшая цифра u помещается в самую правую позицию. В Форт-83, если число символов, необхо¬
димое для отображения u, больше n, дается сообщение об ошибке. Ширина поля меньше 1 запреще¬
на. В Форт-79, если n меньше числа символов, число будет отображено, но без предшествующих
пробелов.
U/MOD ”u-divide-mod” **U-разделить с остатком** 79REQ
( udl ul — u2 u3)
Делит число двойной длины udl на ul и засылает в стек остаток одинарной длины u2 и частное
одинарной длины u3. Применено деление с нижней границей. Все величины рассматриваются как
числа без знака. Синоним U/MOD в Форт-83.
U< ”u-less-than” **U-меныие” 83REQ 79REQ
( ul u2 — флаг)
Сравнивает ul и u2 и заносит в стек флаг истинно, если ul меньше чем u2.
UD.”u-d-dot” тхт
( d -)
Отображает число двойной длины без знака, лежащее в стеке, в соответствии с величиной BASE.
После числа вводится пробел.
: UD. <# #S #> TYPE SPACE ;
267
UD.R "u-d-dot-r" TXT
( ud n —)
Отображает число двойной длины без знака, лежащее в стеке, в поле шириной n позиций. Причем
младшая цифра числа помещается в самую правую позицию.
UM* "u-m-times" ”U-M-умножить” 83REQ
( ul u2 — ud)
Перемножает числа ul и u2 и заносит в стек произведение двойной длины ud. Все величины рас¬
сматриваются как числа без знака. Синоним U* в Форт-79.
UM/MOD ”u-m-divide-mod” ”UM-разделить с остатком” 83REQ
( ud ul — u2 u3)
Делит число двойной длины без знака ud на ul и заносит в стек остаток одинарной длины u2 и час¬
тное одинарной длины u3. Все величины рассматриваются как числа без знака. Синоним U/MOD в
Форт-79.
UNTIL ’’until” I, С 83REQ 79REQ
( флаг —)
Отмечает конец бесконечного цикла. При использовании в форме
: <name> ... BEGIN ... флаг UNTIL ... ;
компилирует условный переход назад к соответствующему BEGIN. Когда <name> исполняется, сло¬
во UNTIL предполагает наличие в стеке флага и, пока флаг равен 0, передает управление назад к
соответствующему слову BEGIN. Если флаг не равен 0, исполнение продолжается со слова после
UNTIL. См. также BEGIN; WHILE; REPEAT.
UPDATE "update” ”поместить” 83REQ 79REQ
( -)
Помечает блочный буфер как модифицированный и готовый для записи в массовую память. Блоки
в буферах, помеченные таким образом, автоматически переносятся в массовую память, когда буфер
требуется повторно. Помеченные блоки могут быть записаны в массовую память с помощью опера¬
тора FLUSH или SAVE-BUFFERS. EMPTY-BUFFERS ликвидирует статус пометки и уничтожает
приписку всех блочных буферов, отменяя воздействие оператора UPDATE.
VAL "val" MMS
( адр — n)
Преобразует числовые символы, лежащие в счетной строке по адресу ”адр”, в число одинарной
длины n в соответствии со значением слова BASE. См. также STR$.
VARIABLE "variable” ”переменная” 83REQ 79REQ
( -)
Слово-описатель, которое создает переменную одинарной длины. При использовании в форме
VARIABLE <name>
формирует в словаре статью с именем <name> и резервирует место в памяти для числа одинарной
длины. Когда <name> исполняется, в стек заносится адрес поля параметров слова <name>, пригод¬
ный для использования @ и !. Переменной не обязательно присваивается какое-либо начальное
значение. См. также CVARIABLE; 2VARIABLE; 4VARIABLE.
VLIST ”v-list” мполный список” FIG
( -)
Отображает список всех слов в текущем (CURRENT) словаре. В настоящее время для этого слова
предпочтительнее имя WORDS.
VOCABULARY "vocabulary" ”конпыкстный
словарь” 83REQ 79REQ
(-)
Слово-описатель, которое создает новый контекстный словарь. При использовании в форме
VOCABULARY <name>
формирует в словаре слово с именем <name>, которое специфицирует новый список описаний слов.
В зависимости от версии <name> может быть, а может и не быть словом немедленного исполнения.
После исполнения <name> контекстный словарь <name> станет первым просматриваемым (текущим
словарем), <name> может быть сделан контекстным словарем, куда заносятся новые описания
(CONTEXT-словарем) с помощью
<name> DEFINITIONS
что приведет к тому, что новое описание будет включено в маршрут поиска <name>. Подробности
настр. 196.
WBLK "write-block" MMS
268
( адр n —)
Записывает 1024 байта, начиная с адреса ”адр”, в блок с номером n. См. также BLK.
WHILE ’’while” I,C 83REQ 79REQ
( флаг —)
Решает прервать или продолжить цикл BEGIN ... WHILE ... REPEAT. При использовании в форме
: <name> BEGIN ... флаг WHILE ... REPEAT ... ;
компилирует оператор условного перехода в описание <name>. Когда <name> исполняется, WHILE
предполагает наличие в стеке флага, пока флаг не равен 0, слова между WHILE и соответствую¬
щим REPEAT выполняются, а REPEAT возвращает управление слову, стоящему после соответству¬
ющего BEGIN. Если же флаг равен 0, управление передается слову, следующему за REPEAT.
WIDTH ’’width” "ширина" FIG MVP
( -- адр)
Переменная, содержащая число символов, которое будет скомпилировано в поле имени описания, т.
e. максимальная длина имени слова. Слово WIDTH можно использовать для ограничения длин
имен слов с целью экономии места в словаре. По умолчанию максимально допустимая длина имени
равна 31 символу.
WORD ”word” "слово" 83REQ 79REQ
( n — адр)
Генерирует счетную строку, извлекая последовательность символов из выходного потока, не видо¬
изменяя его, до тех пор, пока не встретится разграничитель, ASCII-код которого равен n, или пока
не иссякнет входной поток. Не вызывает прерывания исполнения. Разграничители, предшествую¬
щие строке, игнорируются. Символы запоминаются в счетной строке, ничиная с адреса ”адр”. (В
большинстве версий ”адр” равен HERE.) Примеры и обсуждения см. на стр. 117.
WORDS ’’words” "слова” 83REQ 79REQ
( ~)
Отображает список всех слов в текущем словаре. Контекстная форма представления варьируется от
версии к версии. Синоним VLIST.
XOR ”х-ог” ”Исключающее ИЛИ" 83REQ 79REQ
( nl n2 —пЗ)
Выполняет побитовую операцию исключающее -ИЛИ для кодов nl и n2, результат n3 заносится в
стек. Другими словами, каждый бит кода nl сравнивается с соответствующим битом кода n2 и, если
один из них (но не оба) равен 1, приравнивается 1 и соответствующий бит n3, в противном случае
он обнуляется. Так, если записать в двоичной форме
110 100 XOR
в результате будет получено 010. См. также AND; OR.
Y/N ”y-slash-n” MMS
( - n)
Отображает запрос ”(Y/N)?” и ожидает ввода с клавиатуры ”Y” или ”N”. Если одна из букв введе¬
на, она будет отображена на экране, а в стек будет занесен флаг (0 для ”Y” и 1 для ”N”). Y/N ис¬
пользуется для управления исполнением с пульта.
[ ”left-bracket” "левая скобка” I 83REQ 79REQ
( -)
Устанавливает систему в режим исполнения. Текст входного потока при этом исполняется, а не
компилируется. При использовании в описаниях типа двоеточие в форме:
: <name> ... [ ... ] ... ;
позволяет исполнить слова между [ и ] при компиляции слова <name>. См. также ]; STATE.
[’] ”bracket-tick” I,C 83REQ
( - адр)
Засылает в стек и компилирует адрес поля программы слова в описании типа двоеточие. При ис¬
пользовании в форме
: <namel> ... (’) <name2> ... ;
компилирует адрес поля программы ”адр” слова <name2> в качестве литерала в описание слова
<namel>. Когда <namel> исполняется, ”адр” засылается в стек и может быть использован, напри¬
мер, оператором EXECUTE. Если <name2> не может быть найдено в словаре, дается сообщение об
ошибке. Выполняет функцию, эквивалентную слову ’ в Форт-83, которое используется только в ре¬
жиме выполнения. В Форт-79 функция ’ зависит от режима и выполняется так же как и [’].
[COMPILE] "bracket-compiIe" I,C 83REQ 79REQ
269
(-)
Заставляет компилироваться слово немедленного исполнения. При использовании в форме
: namel> ... [COMPILE] <name2> ... ;
компилирует слово немедленного исполнения <name2>, которое обычно исполняется даже в режиме
компиляции. В отличие от слова COMPILE, которое имеет совсем иную функцию, само
[COMPILE] не компилируется.
\ ’’backslash” I TXT MVP VAR
( -)
Вынуждает Форт-интерпретатор игнорировать остальную часть 64-символьной строки. Позволяет
использовать оставшуюся часть строки для комментариев. Может использоваться внутри описаний
типа двоеточие. Описывается как
: \ >IN @ 63 OR 1+ >IN ! ; IMMEDLATE
: \ >IN @ 64 / 1+ 64 * >IN ! ; IMMEDLATE
См. также ( .
] ’’right-bracket” ”правая скобка” 83REQ 79REQ
( -)
Устанавливает систему в режим компиляции. Текст входного потока будет после этого компилиро¬
ваться. Слово ] удобно для компиляции слов без заголовка и для компиляции CFA слов в массив
для векторного исполнения. См. также [; STATE.
ПРИЛОЖЕНИЕ Б
ГЛОССАРИЙ ФОРТА
Терминология
Этот глоссарий включает в себя терминологию Форта, используемую в этой книге, а также общепринятую терминологию
из области технологии ЭВМ. Заметьте, что применение и значение определенных терминов в Форте отличаются от общепри¬
нятых в вычислительной технике. Глоссарий предназначен для помощи при работе с текстом книги, он не является полным
и исчерпывающим.
16-bit-number. 16-битовое число. Число (со знаком или без), которое может быть записано в 16 битов памяти. 16-битовое
число без знака может представлять числа в диапазоне 0 — 65.535, а со знаком — в интервале -32.768 — 32.767. Для пред-
ствления чисел со знаком используется дополнение по модулю два. См. также числа со знаком; числа без знака; дополнение
по модулю два.
32-bit-number. 32-битовое число. Число (со знаком или без), которое может быть записано в 32 битах памяти. 32-битовое
число без знака может представлять число в диапазоне 0 — 4.294.967.295, а со знаком — в интервале -2.147.483.648 —
2.147.483.647. Для представления чисел со знаком используется дополнение по модулю два. См. также числа со знаком;
числа без знака; дополнение по модулю два.
Address. Адрес. Позиция в памяти ЭВМ. Адреса в большинстве микро-ЭВМ нумеруются байт-за-байтом, начиная с 0 до пре¬
дельного адреса, который задается разрядностью адресной шины. Восьмибитовые микропроцессоры, такие как Z-80 и 8080,
имеют 16 адресных шин и предельный адрес 65.535 (216). Шестнадцатибитовые микропроцессоры, такие как 8086, 8088 и
68000 часто имеют 20, 24 или даже 32 адресные шины и могут использовать очень большие памяти. Выражение ’’Z-80 мо¬
жет адресоваться к 65535 байтам памяти” означает, что максимальное значение адреса равно 65534.
Address interpreter. Интерпретатор адресов. Машинная программа Форта, которая осуществляет исполнение программы пу¬
тем пошагового перехода по списку адресов других машинных программ, включающих в себя откомпилированные Форт-про¬
граммы. Называется также внутренним интерпретатором, в противоположность внешнему интерпретатору. См. также поле
параметров.
Address space. Адресное пространство. Область памяти ЭВМ, к которой ЭВМ может адресоваться непосредственно. B боль¬
шинстве микропроцессоров адресное пространство ограничено основной (полупроводниковой) памятью, но в ЭВМ с вирту¬
альной памятью адресное пространство может распространятся на массовую память.
Algebraic notation. Алгебраическая нотация. Обычная нотация в алгебре. Система отображения математических соотноше¬
ний, использующая произвольные символы в качестве операндов, инфиксную нотацию для операторов и скобки для задания
очередности операций. В более широком толковании — любое выражение, использующее те же правила формирования.
Противостоит постфиксной и прсфиксной нотации.
270
Algorithm. Алгоритм. Набор правил, которые определяют, как достичь желаемого результата. Все программы ЭВМ явно или
неявно написаны так, чтобы реализовать один или несколько алгоритмов.
Argument(s). Аргумент(ы). Величина (или величины), необходимая программе для выполнения ее функций. В Форте аргу¬
менты помещаются в стек параметров для последующего использования их Форт-словами. Если слово CUBE в Форте описа¬
но так, чтобы вычислять куб чи£ла, то число 5 в примере
5 CUBE
является аргументом. См. также операнд.
Arithmetic coprocessor. Арифметический сопроцессор. Специальная микросхема, используемая совместно с центральным
процессорным устройством для ускоренного вычисления арифметических и трансцендентных функций. Арифметический со¬
процессор 8087 спроектирован для совместной работы с процессорами 8086 и 8088.
Arithmetic overflow. Арифметическое переполнение. Ошибка, которая происходит, когда арифметическая операция выдает
результат, не соответствующий разрядности памяти. Например, если два числа одинарной длины перемножены с помощью *
и получен результат более 65535 (2|6-1), этот результат ошибочен, так как * игнорирует часть произведения, не укладываю¬
щуюся в 16 битов. В случае работы с операторами смешанного типа для результата будет выделено 32 разряда. См. также
числа одинарной длины; числа двойной длины.
Array. Массив. Совокупность чисел, строк или другой информации, где каждый информационный объект называется эле¬
ментом, а элементам поставлены в соответствие числа или индексы. Вектор представляет собой одномерный или линейный
массив с одним индексом, матрица — двумерный массив, уложенный в ряды и столбцы с двумя индексами (хотя матрица с
одним рядом идентична вектору); массивы большей размерности также возможны.
ASCII. Обозначение для Американского стандартного кода для обмена информацией. ASCII первоначально был разработан
для стандартизации обмена между телетайпными терминалами. ASCII-код определяет набор управляющих символов (со зна¬
чениями 0 — 31 и 127), чисел, букв и других символов (со значениями 32 — 126). (См. приложение Д, где приведена таб¬
лица ASCII-символов.) Заметим, что ASCII определяет только набор символов для младших 7 битов байта, так что коды
между 128 и 255 8-битового байта не определены в ASCII и их использование на разных ЭВМ сильно варьируется.
Assembler language. Ассемблер. Язык программирования низкого уровня, использующий мнемонику, которая может быть
непосредственно транслирована в исполняемую машинную программу. Форт-ассемблер состоит из слов словаря ASSEMBLER,
которые описывают мнемонику и компилируют машинные программы. См. пшкже набор инструкций, машинные инструк¬
ции.
Base (number). Основание (числа). См. основание системы счисления.
Baud rate. Скорость передачи (в бодах). Телекоммуникационный термин, часто используется неверно в значении "бит в се¬
кунду”. Скорость передачи в бодах в действительности равна числу раз в секунду, когда телекоммуникационный канал ме¬
няет свое состояние, что в зависимости от схемы кодирования может давать число битов в секунду значительно больше, чем
скорость передачи в бодах.
Baudot code. Код Бодо. Старый телекоммуникационный код, названный в честь Эмиля Бодо. В этом коде представления
символов на телетайпе используется только 5 битов в верхнем и нижнем регистрах (поэтому возможно представление более
чем 32 символов). Код Бодо используется также для некоторых других целей, таких как передача данных о погоде и для
связи между глухими.
Binary notation. Двоичное представление. Система представления чисел, использующая основание 2. Существует только две
двоичные цифры, 0 и 1. Внутреннее представление чисел в ЭВМ только двоичное, так как бистабильные электронные схемы
хорошо приспособлены для представления двоичных цифр. Вход и выход системы Форта может быть сделан двоичным с по¬
мощью
2 BASE !
См. также бит.
Bit. Бит. Самая малая единица информации, имеющая только два состояния 0 и 1. Бит — это сокращение термина ”binary
digit” (двоичная цифра) и т.о. является цифрой двоичного представления. См. также байт.
Bit mask. Бит-маска. Число, используемое для извлечения или изменения битов другого числа. Бит-маски широко использу¬
ются для манипулирования битами в памяти, а также используются с булевыми операторами, позволяя в одном байте хра¬
нить восемь отдельных флагов. Называется также просто маской.
Block. Блок. В Форте — часть массовой памяти, которая содержит 1024 (lK) байтов. Блоки нумеруются последовательно и
загружаются в память словом BLOCK. Для переноса блока из памяти на диск используется UPDATE и FLUSH (или SAVE-
BUFFERS). Называется также экраном, в особенности если содержит только алфавитно-цифровую информацию. См. также
блочный буфер; кэш диска; виртуальная память.
Block buffer. Блочный буфер. Область в основной памяти для хранения блоков Форта при обмене с диском. Блок заносится
в блочный буфер с помощью слова BLOCK, которое оставляет в стеке адрес первого байта блочного буфера. Активный в дан¬
ный момент блочный буфер помечается для последующей переписи на диск с помощью слова UPDATE. Перенос осуществ¬
ляется при необходимости перезаписи в блочный буфер или при использовании слов FLUSH или SAVE-BUFFERS. См.
также блок; кэш диска; виртуальная память.
271
Boolean flag. Булев флаг. Число, используемое для описания логического состояния. Истинно в Форт-79 равно 1, в то вре¬
мя как в Форт-83 1 (FFFF HEX или все биты равны 1). В обоих стандартах 0 соответствует ложно. Условные операторы
Форта распознают любое ненулевое число в стеке как булев флаг истинно. Часто называется просто флагом. Назван в честь
британского математика Джоржа Буля.
Buffer. Буфер. Область памяти, зарезервированная для временного хранения информации. В Форте блочные буферы исполь¬
зуются для пересылки 1024 байтов с диска и на диск, а входной буфер воспринимает данные, поступающие с клавиатуры.
Byte. Байт. Группа из 8 битов, которая чаще всего рассматривается как элемент памяти. Байт может принимать значение 0
— 255 (в десятичном представлении) или 0 — FF в шестнадцатеричном. См. также бит; килобайт; мегабайт.
Case-construct. CASE-конструкция. Структура программы, которая позволяет при исполнении осуществлять переход на одну
из нескольких программ в зависимости от того, какая совокупность условий удовлетворена. CASE-конструкция может быть
реализована через систему встроенных структур IF...ELSE...THEN, но часто используются специальные Форт-слова, такие
как ACASE и NCASE в MMSFORTH. См. также векторное исполнение.
Cell. Ячейка. В Форте размер ячейки равен 16 битам. Ячейка в Форте предпочтительнее, чем более распространенный тер¬
мин ”слово” (что означает для большинства микро ЭВМ то же самое), чтобы избежать путаницы с Форт-термином "слово”.
Ячейка может содержать ровно одно число одинарной длины. См. также длина слова.
Central processing unit. Центральный процессор. Часть ЭВМ, которая интерпретирует и исполняет машинные инструкции.
В микро ЭВМ — это одна микросхема, называемая микропроцессором. Часто используется сокращение ЦП, иногда называ¬
ется просто процессор.
CFA. Обозначение для адреса поля программы.
Code-field. Поле программы. Часть откомпилированного Форт-описания, содержимое которого указывает на машинную про¬
грамму, которая исполняется, когда исполняется описанное слово. См. также адрес поля программы.
Code-field-address. Адрес поля программы. Адрес поля программы откомпилированного Форт-описания. Поле параметров
типа двоеточие содержит список адресов полей программы слов, содержащихся в описании, но не адрес, содержащийся в
поле программы. Обычно обозначается сокращением CFA. См. также поле программы.
Colon-definition. Описание-двоеточие. Описание слова Форта, созданное оператором :. Часто используется синоним слово-
двоеточие.
Colon-word. Слово-двоеточие. Слово Форта, описанное словом описателем :. Слова-двоеточие, кстати, являются наиболее
часто встречающимися словами Форта. Часто используется синоним описание двоеточие.
Comparison operator. Оператор сравнения. Оператор, который определяет соотношение между двумя числами и выдает бу¬
лев флаг, характеризующий это соотношение. Например, оператор сравнения может определить, является ли величина мень¬
ше, больше или равна 0, меньше, больше или равна другой величине. Операторы сравнения могут комбинироваться, чтобы
сформировать такие проверки, как меньше или равно, больше или равно и т. д. Примерами могут служить слова Форта <, >,
- и 0".
Compilation. Компиляция. Процесс трансляции исходного текста программы в форму, пригодную для исполнения на ЭВМ. В
большинстве языков (таких как Фортран) компиляция состоит в формировании машинной программы из исходного текста
высокого уровня. Описания-двоеточие в Форте при компиляции образуют список адресов полей программы, которые указы¬
вают интерпретатору исполнительные адреса. Мнемоника Форт-ассемблера компилируется в машинные инструкции, которые
ЭВМ может исполнить немедленно.
Compile mode. Режим компиляции. Режим, при котором внешний интерпретатор Форта компилирует слова в Форт-описа¬
ния, а не исполняет их. Это происходит, когда переменная STATE не равна 0. Если в режиме компиляции встретится слово
немедленного исполнения, оно выполняется. Противостоит режиму исполнения.
Compiled code. Откомпилированная программа. Результат компиляции. Ассемблер компилирует выполнимую машинную
программу. Форт-компилятор формирует список адресов, который может исполнить интерпретатор адресов. См. также ре¬
жим компиляции; поле параметров.
Concatenate. Присоединить. Сформировать ”стык-в-стык” цепочку из строк, списков величин или других наборов информа¬
ции.
Conditional operator. Условный оператор. Элемент программы, который вызывает передачу управления (ветвление) по ука¬
занному адресу или выполнение заданной функции, если выполнено определенное условие. Примерами из Форта могут слу¬
жить IF, WHILE, UNTIL и ABORT”, каждый из которых предполагает наличие в стеке флага, который и определяет, что
делать. См. также булев флаг.
Control character. Управляющий символ. ASCII-символ, который выполняет управление терминалом. ASCII-символы имеют
коды от 1 до 31 и 127. Например, слово Форта CR выдает символ ”возврат каретки” или 13. См. также ASCII; управляю¬
щая клавиша. См. приложение Г.
Control key. Клавиша CTRL. Клавиша, расположенная на терминале ЭВМ и большинства микрокомпьютеров, которая при
совместном нажатии с другой клавишей формирует ASCII-CTRL- символ. Например, CTRL H выдаст ASCII-код 8 (возвра¬
щение курсора на одну позицию). См. приложение Г.
Controlled reference word. Слово из контролируемого списка. В стандарте Форт-83 слова, которые, хотя и не входят в обя¬
зательный список, если присутствуют в стандартной системе, должны быть описаны в соответствии со спецификациями
272
стандарта. Противоположно слову из расширяющего набора слов; слову из обязательного списка и словам из ’’неконтролиру¬
емого” списка.
Count (string). Счетчик (строки). Байт, размещенный непосредственно перед строкой алфавитно-цифровых символов и со¬
держащий число символов в строке (0-255). См. также счетная строка.
Counted string. Счетная строка. Формат, обычно используемый в Форте для записи строк символов. Счетная, строка состоит
из байта-счетчика (однобайтовое число), за которым непосредственно следует строка символов. Байт-счетчик определяет
число символов в теле строки. См. также счетчик (строки).
CPU. ЦПБ. Сокращение» означающее ’’центральный процессорный блок.”
Crosscompiler. Кросскомпилятор. Программа, которая позволяет откомпилировать программу на одной ЭВМ так, что она
сможет работать на другой. В Форте разновидностью кросскомпилятора является метакомпилятор или целевой компилятор,
который позволяет преобразовывать всю систему Форта так, что она сможет работать на другой ЭВМ, часто со специальны¬
ми функциями.
Data stack. Стек данных. Синоним стека параметров.
Decimal notation. Десятичная система представления. Представление чисёл в привычной десятичной системе счисления, ко¬
торая использует цифры 0 — 9. В Форте десятичный ввод и вывод задаются словом DECIMAL.
Decompiler. Декомпилятор. Программа Форта, которая ’’декомпилирует” откомпилированные описания-двоеточие так, что
они приобретают вид, напоминающий исходный текст. Декомпилятор показывает, как были описаны откомпилированные
слова.
Deferred compilation. Отложенная компиляция. Термин Форта, описывающий действие COMPILE и других родственных
слов. Когда внутри слова-двоеточие исполняется оператор COMPILE, он компилирует CFA слова, следующего за ним, в опи¬
сание слова-двоеточие таким образом, что, когда слово-двоеточие исполняется, это CFA будет скомпилировано в словарь. Та¬
ким образом компиляция слова, следующего за COMPILE, откладывается до исполнения COMPILE. Например, LITERAL
содержит COMPILE LrT, причем это слово немедленного исполнения, так что, когда LrTERAL встречается в описании, оно
компилирует LIT и число из стека в словарь.
Defining word. Слово-описатель. Слово Форта, которое служит для описания новых (производных) слов. Любое слово-двое¬
точие, которое описано с помощью CREATE, является словом-описателем. Общеизвестными примерами могут служить
CONSTANT и VARIABLE. Новые слова-описатели могут быть созданы с помощью конструкции CREATE...DOES>.
Delimiter. Разделитель. Любой код, обычно ASCII-символ, который используется для выделения элементов данных. Напри¬
мер, слово .” использует в качестве разделителя ” (двойные кавычки), а слово ( использует ) (закрывающую скобку). Форт
распознает,пробел (ASCII 32) в качестве разделителя между Форт-словами и числами во входном потоке. Для отделения по¬
следовательностей символов различной длины в файле могут использоваться один или несколько разделителей. См. также
разбор.
Dictionary. Словарь. В Форте — это связанный список откомпилированных описаний, которые могут быть исполнены интер¬
претатором адресов Форта. Читаемый ЭВМ набор слов Форта, которые могут быть исполнены или откомпилированы. Форт
помечает начало словаря и адрес его ’’вершины”, последний заносится в стек с помощью HERE.
Dictionary pointer. Указатель словаря. Переменная (иногда называемая DP), значение которой указывает на первый свобод¬
ный байт выше словаря. Значение указателя словаря засылается в стек оператором HERE.
Disassembler. Дисассемблер. Служебная программа, которая отображает машинную программу в читаемой форме мнемони¬
ки ассемблера. Дисассемблер рассматривает группу из одного или нескольких байтов и отображает мнемонику ассемблера и
аргументы, которые были бы необходимы для их генерации.
Disk cache. Кэш диска. Метод управления дисковой памятью, в котором часто используемая информация находится в памя¬
ти, а не считывается с диска каждый раз, когда необходима. Блочный буфер Форта является разновидностью кэш-буфера.
См. также виртуальная память; буфер; блок.
Disk operating system. Дисковая операционная система. Набор программ или служебных средств для обеспечения взаимо¬
действия между оператором, ЭВМ и другими элементами, такими как принтер, интерфейс RS-232 и дисковая память. Дис¬
ковая операционная система контролирует также многие функции ЭВМ. СР/М и MS-DOS являются типичными примерами
ДОС для микроЭВМ. Автономная версия Форта несет в себе элементы дисковой операционной системы.
Do-loop. do-loop. Программная конструкция, которая заставляет определенную часть программы выполняться заданное число
раз. do-loop в Форте реализуется с помощью DO...LOOP или DO...+LOOP. См. также цикл; бесконечный цикл.
DOS. ДОС. Сокращение для ’’дисковая операционная система”.
DoubIe-length-number. Число двойной длины. Целое 32-разрядное число. Числа двойной длины без знака лежат в диапазо¬
не 0 — 4.294.967.295, а числа двойной длины со знаком — от 2.147.483.648 до 2.147.483.647. Называются также двойными
числами и числами двойной точности (хотя последнее нетипично, это должно относиться только к числам с плавающей за¬
пятой). Противоположно числам одинарной длины.
Double-number. Двойное число. Синоним числа двойной длины, что предпочтительнее.
Double-precision number. Число двойной точности. Синоним числа двойной длины, последний термин предпочтительнее.
Более правильно — число с плавающей запятой с двойным числом значащих цифр.
273
Edilor. Редактор. Программа для ввода и модификации данных, обычно исходного текста программы. Форт-редактор исполь¬
зуется для модификации содержимого блоков Форта.
Elcmcnt (аггау). Элемент (массива). Часть массива, обычно число или строка, доступ к которой возможен с помощью ин¬
декса массива. См. также индекс (массива).
Escape key. Клавиша ESC. Клавиша, присутствующая на многих терминалах для инициации ESC-последовательностей. Она
формирует ASCII код 27.
Escapc-scgucncc. ESC-последователыюсть. Последовательность символов, начинающая с символа ESC (ASCII 27). ESC-no-
следоватсльность используется для управления внешними устройствами, такими как терминал или принтер. См. также кла¬
виша ESC.
Executc modc. Режим исполнения. Режим работы в Форте, в котором слова, встретившиеся во входном потоке, немедленно
исполняются и в котором числа кладутся в стек, а не компилируются. Форт устанавливается в режим исполнения, когда зна¬
чение переменной STATE равно 0. Противоположен режиму компиляции.
Execution. Исполнение. Действие, при котором ЭВМ выполняет операции, заданные программой (или Форт-словом). Ис¬
полнение в Форте происходит, когда значение переменной STATE равно 0, и осуществляется интерпретатором адресов. См.
также режим исполнения; режим компиляции.
Exponential notation. Показательное представление. Способ представления чисел с плавающей запятой в виде мантиссы и
показателя степени. Так, число с плавающей запятой 1024. может быть представлено как 1,024*103 или, как обычно, на вы¬
ходе ЭВМ 1,024E3, 1.024 — мантисса, а 3 — показатель. Показатель равен показателю степени, в которую нужно возвести
10, после чего умножить результат на мантиссу, чтобы получить правильную величину числа. Научная нотация является
формой показательного представления.
Extension word set. Расширяющий набор слов. Набор слов, которые могут быть добавлены к системе для расширения ее
возможностей. Как в стандарте Форт-79, так и в Форт-83 эти слова не являются обязательными, но если они вводятся в сис¬
тему, они должны соответствовать стандартному описанию. Стандартные расширяющие наборы включают в себя библиотеку
для работы с числами двойной длины, ассемблер и системные расширения. Нестандартные наборы часто содержат графику,
библиотеку для работы с числами с плавающей запятой, декомпилятор, слова для работы со строками и т. д.
Extensible language. Расширяемый язык. Любой язык, такой как Форт, который может расширить свои возможности с по¬
мощью своих собственных средств, в противоположность таким языкам, как Фортран, Паскаль или Бейсик, которые исполь¬
зуются для написания программ, не имеющих ничего общего с самим языком.
Factoring. Факторизация. В Форте процесс разбиения задачи на более мелкие задачи, которые могут быть эффективно за¬
программированы на Форте. Длинные описания слов могут (и обычно должны) быть для ясности разложены не несколько
более коротких описаний. Этот процесс аналогичен факторизации сложных уравнений в набор более простых.
False (flag). Ложно (флаг). Булев флаг, который устанавливается, когда при проверке выясняется, что некоторое условие не
выполняется. Противоположно флагу истинно. См. также условный оператор.
Field (file). Поле (в файле). Заданная область памяти в пределах записи в файле, обычно содержащая один элемент инфор¬
мации. В адресном файле, например, каждая запись может содержать поля для имени, адреса, улицы, города, страны и поч¬
товый индекс.
File. Файл. Упорядоченная совокупность данных в массовой памяти. Файл обычно состоит из одной или более записей,
каждая из которых содержит одно или более полей.
Flag. Флаг, число, часто один бит, которое индицирует состояние определенного условия. Если флаг имеет только два воз¬
можных состояния, называемых истинно и ложно, то это булев флаг.
Flag register. Регистр флагов. Регистр ЦПБ, разряды которого индицируют условия, соответствующие результату выполне¬
ния самой последней инструкции, например произошло или нет арифметическое переполнение. Разряды этого регистра ана¬
лизируются при выполнении условных операций машинной программы.
Floating-point notation. Представление чисел с плавающей запятой. Способ показа того, что число имеет целую и дробную
части: целую часть — слева от десятичной запятой и дробную часть — справа. Так, 12,34 равно 12 + 34/100. Положение де¬
сятичной запятой корректируется автоматически (плавает) в зависимости от результата арифметической операции. См. так¬
же показательное представление.
Flowchart. Блок-схема. Диаграмма, которая графически демонстрирует процесс выполнения программы. Блок-схема является
часто хорошим способом прояснить структуру программы.
FORTH*standart. Стандарт Форта. Минимально необходимый набор слов Форта и их специфированных функций, а также
описанных функций слов, которые не являются обязательными. Цель стандарта — позволить стандартным Форт-программам
работать на самых разных ЭВМ. Двумя главными стандартами Форта являются Форт-79 и Форт-83, были предложены и
другие более ранние стандарты, но они не получили широкого распространения. См. пшкже слово из контролируемого спи¬
ска; слово из расширяющего набора; слово из обязательного списка; слово из неконтролируемого списка.
Hash code. Хэш-код. См. хэширование.
Hashing. Хэширование. Метод, при котором входной код используется для того, чтобы определить, где этот код должен быть
записан в память или файл. Определяется хэш-код, который используется для вычисления адреса в памяти. Тот же самый
хэш-код (и, следовательно, тот же адрес) генерируется при поиске кода, ускоряя таким образом доступ к информации.
274
Хэширование — 3to метод кодирования строки или числа, обычно для того, чтобы сэкономить память. В Форте хэширование
используется лишь иногда, чтобы приписать слова к различным маршрутам поиска, таким образом ускоряя поиск в словаре
и компиляцию.
HEX. Жаргонное слово, обозначающее шестнадцатеричную систему представления.
Hexadecimal notation. Шестнадцатеричное представление. Представление чисел при основании системы счисления 16. Ше¬
стнадцатеричные цифры лежат в диапазоне 0 — 9 и для представления десятичных чисел от 10 до 15 — от А до F. 31 в де¬
сятичном представлении равно lF в шестнадцатеричном. Противостоит двоичному, восьмеричному и десятичному пред¬
ставлению.
High-level language. Язык высокого уровня. Языки программирования (такой как Бейсик, Кобол, Фортран и, конечно,
Форт), которые используют слова и (в какой-то степени) синтаксис человеческого языка. Все языки высокого уровня долж¬
ны идти на некоторый компромисс между использованием памяти и быстродействием с целью облегчения программирования
для людей. Форт требует меньшего компромисса, чем большинство других языков. Противостоит языкам низкого уровня.
Immediote word. Слово немедленного исполнения. Слово Форта, которое будет исполнено даже в режиме компиляции, на¬
пример, во время компиляции описания. Слова немедленного исполнения помечаются с помощью установки специального
бита в заголовке. Примерами таких слов могут служить (, IF, BEGIN, .(, и LITERAL. См. пшкже отложенная компиляция;
лидирующий бит.
Incremental compiler. Инкрементный компилятор. Тип компилятора, который интерпретирует исходный текст элемент-за-
элементом и последовательно формирует исполняемую машинную программу.
Компиляция в Форте производится инкрементно.
Indefinite loop. Бесконечный цикл. Программная структура, которая вызывает повторное исполнение выделенной части про¬
граммы бесконечное число раз, пока не будет выполнено определенное условие. BEGIN...WHILE...REPEAT и
BEGIN...UNTIL являются конструкциями, организующими бесконечные циклы в Форте. См. также цикл. Противостоит do-
loop.
Index (array). Индекс (массива). Одно или более чисел, которое определяет номер элемента массива. См. также вектор;
матрица.
Index (loop). Индекс (цикла). В структуре do-loop счетчик числа циклов. В DO...LOOP в Форте значение индекса заносится
в стек параметров оператором I.
Index Iine. Индексная строка. Верхняя строка блоков Форта, согласно договоренности используется для комментариев содер¬
жимого блока, таких как дата, автор, список описанных слов и т. д.
Infix notation. Инфиксная нотация. Представление математических функций, при котором операторы помещаются между
операндами. Алгебраическая нотация является формой инфиксной нотации. Инфиксная нотация требует использования ско¬
бок для определения порядка операций. Противостоит префиксной и постфиксной нотации.
Inner interpreter. Внутренний интерпретатор. Короткая машинная программа, которая осуществляет выполнение скомпили¬
рованного списка адресов-заданий из описаний словаря Форт. Синоним интерпретатора адресов. См. также поле програм¬
мы; поле параметров.
Input stream. Входной norotf. Термин Форта для исходного текста программы или данных, которые интерпретируются в дан¬
ный момент внешним интерпретатором Форта. Входной поток поступает с клавиатуры, когда значение переменной BLK рав¬
но 0, в противном случае — из блока, номер которого лежит в BLK. Форт-определение входного потока отличается от обще¬
признанного в вычислительной технике, где входной поток является синонимом потока заданий, списка задач, ожидающих
исполнения, обычно на главном процессоре.
Instruction set. Набор инструкций. Полный набор команд машинного языка (двоичных чисел в памяти), которые может ис¬
полнить центральный процессор. Полный ассемблер включает мнемонику полного набора инструкций процессора.
Interpretation. Интерпретация. В Форте существует два различных значения, которые следует понимать из контекста. В од¬
ном случае интерпретация — это процесс, при котором исходный текст программы или данных воспринимается из входного
потока внешним или текстовым интерпретатором и обрабатывается. Если система Форт находится в режиме исполнения,
слова немедленно исполняются, а числа заносятся в стек. В режиме компиляции входные данные используются для описания
новых слов. Под интерпретацией понимается также декодирование и исполнение слов, описанных в словаре Форта, что про¬
изводится внутренними или адресным интерпретатором. Эти значения отличаются от общепринятых в вычислительной тех¬
нике, где под *’интерпретацией” подразумевается работа интерпретатора, при которой в случае языков высокого уровня ис¬
ходный текст исполняется элемент за элементом через машинные программы без формирования полного объектного образа
программы (Бейсик использует обычно интерпретатор). Поскольку Форт способен как немедленно исполнять слова при их
вводе, так и компилировать новые слова и программы, он сочетает в себе преимущества интерпретатора и компилятора,
обеспечивая немедленную реакцию первого и быстродействие последнего. См. также цепной интерпретивный язык.
Interpreter. Интерпретатор. В вычислительной технике применения языка высокого уровня, в котором исходный текст пре¬
образуется в машинную программу и исполняется элемент за элементом без построения полного объектного образа програм¬
мы (Бейсик использует обычно интерпретатор). В Форте смысл сходен, но отличен как в случае внешнего (текстового), так
и внутреннего (адресного) интерпретатора. Первый воспринимает и исполняет или компилирует текст, в то время как по¬
275
следний декодирует и исполняет описание в Форт-словаре. Форт работает, вообще говоря, как интерпретатор, только когда
входная программа исполняется внешним интерпретатором. См. также интерпретация.
Irrational number. Иррациональное число. Число, такое как "Пи" или e, которое не может быть выражено через какое-ли¬
бо отношение двух целых чисел. См. также рациональное приближение.
Kernel. Ядро. Набор Форт-примитивов (последовательности машинных программ) в "нижней" части словаря, который явля¬
ется базисом всех последующих Форт-описаний.
Kilobyte. Килобайт. 1024 (210) 8-битовых байтов информации, обычно в памяти или на диске. См. также байт; мегабайт.
Last-in-first-out stack. Стековая структура LIFO. Любой стек, где число, введенное последним, окажется выведено первым. В
Форте как стек параметров так и стек возвратов относятся к этому типу.
Least significant bit. Младший бит. Бит любого числа, который является младшим при двоичном представлении. В двоичном
числе 101 самая правая единица является младшим битом.
Least significant byte. Младший байт. Байт любого числа, который является младшим при шестнадцатеричном представле¬
нии. В шестнадцатеричном числе 12AF AF — младший байт.
Least significant digit. Младшая значащая цифра. Цифра в любом числе, которая отображает наименее значимую величину.
Самая правая цифра. В десятичном представлении 5 в 1295 представляет собой младшую значащую цифру.
Least significant number. Младшее число. В Форт-числе двойной длины — это число одинарной длины, которое представ¬
ляет младшую его часть. В шестнадцатеричном представлении F25F в числе 129CF25F является младшим.
LFA. Обозначение для "адреса поля связи".
Linear array. Линейный массив. Структура данных, которые могут быть представлены в виде линейки элементов массива.
Массив с элементами, пронумерованными последовательно от п( до nn с помощью одного индекса. Отдельная строка или ко¬
лонка матрицы является линейным массивом. Вектор. Противоположность матрицы.
Link-field. Поле связи. Часть откомпилированного Форт-описания, которая содержит адрес или связь, определяющие следу¬
ющее слово, которое будет просмотрено при поиске в словаре.
Link-field address. Адрес поля связи. Адрес поля связи в откомпилированном Форт-описании. Содержимое этого адреса
указывает на слово, которое при поиске в словаре будет просмотрено следующим. Но не адрес в поле связи.
Literal. Литерал. В Форте это число, которое скомпилировано в описание-двоеточие (либо в результате распознания числа
во входном потоке, либо словом LITERAL) и которое при исполнении слова, куда оно было скомпилировано, заносится в
стек. В некоторых версиях разрешено использовать в качестве литералов байты и числа двойной длины. В вычислительной
технике это слово имеет другое значение и относится к прямому числовому представлению величины числа в противополож¬
ность символьному представлению. В строке Бейсик А - 5 + 6, 5 и 6 — литералы.
Load block. Загрузочный блок. В Форте блок, используемый для загрузки других блоков. Загрузочный блок является удоб¬
ным средством для контроля за порядком загрузки последовательности блоков, при загрузке, например, различных дополне¬
ний и частей программы.
Loop. Цикл. Программная структура, которая вызывает повторное исполнение определенной части программы. Число повто¬
рений цикла может быть задано (конечные циклы или do-loop) или не определено (бесконечный цикл).
Low-leveI language. Язык низкого уровня. Язык программирования, который транслируется непосредственно или почти на¬
прямую на машинный язык. Ассемблер является наиболее распространенным языком низкого уровня, мнемоника которого
непосредственно преобразуется в машинные инструкции. Форт, позволяя описание слов с использованием ассемблера, ком¬
бинирует преимущества языков высокого и низкого уровней.
LSB — в контексте младший бит или байт какого-либо числа. Machine code. Машинная программа. Набор двоичных кодов,
которые будучи загруженными резидентно в память могут быть непосредственно исполнены центральным процессором. Все
языки в конечном итоге транслируют инструкции высокого уровня в машинную программу.
Machine instruction. Машинная инструкция. Двоичное число в машинной программе, которое предписывает центральному
процессору выполнить специфическую функцию. См. также набор инструкций; машинная программа; машинный язык; ас¬
семблер; мнемоника.
Machine language. Машинный язык. Язык, состоящий из машинных программ, которые непосредственно исполнимы цент¬
ральным процессором. Обычно представляются в виде байтов в шестнадцатеричном счислении. Метод программирования —
путем загрузки набора двоичных кодов, которые могут быть исполнены ЭВМ. Противостоит языку высокого уровня; язы¬
кам низкого уровня; ассемблеру.
Mask (bit). Маска. См. также бит-маска.
Mass storage. Массовая память. Устройство долговременной памяти данных вне основной (полупроводниковой) памяти. Ти¬
пичными устройствами массовой памяти являются гибкий и жесткий магнитные диски, магнитофон и память на магнитных
доменах.
Matrix. Матрица. Двумерный массив, который может быть представлен в виде таблицы данных, изображенной на плоскости.
Элементы образуют строки и столбцы и адресуются с помощью двух индексов. Вектор или линейный массив могут рассмат¬
риваться как матрица (только с одной строкой или столбцом). Квадратная матрица имеет равное число строк и столбцов.
Megabyte. Мегабайт. 1.048.576 (220) 8-битовых байтов в памяти или на диске. См. также килобайт.
276
Memory. Память. Часть ЭВМ, в которой запоминаются данные и программа. Внутренняя память адресуема центральным
процессором непосредственно. Существуют постоянные запоминающие устройства (ПЗУ), содержимое которых центральный
процессор может прочесть, но не может изменить, имеются также запоминающие устройства с произвольной выборкой
(ЗУПВ), для которых осуществимо как чтение, так и запись. Внешняя память содержит информацию, которая должна быть
считана в основную память, прежде чем будет использована процессором. Внешняя память — это массовая память, включа¬
ющая в себя такие устройства, как магнитофоны или диски. См. также адресное пространство; массовая память; виртуаль¬
ная память.
Memory map. Карта памяти. Визуальное представление того, где размещаются различные части системы и как используется
память для тех или иных функций ЭВМ. Карта памяти полезна при описании функций программы, языка или ЭВМ.
Metacompiler. Метакомпилятор. Разновидность кросскомпилятора, которая используется для написания других компилято¬
ров. В Форте метакомпилятор — это Форт-программа, которая может компилировать полную Форт-систему и запускать ее
на той же или другой ЭВМ. Называется также целевым компилятором.
Microprocessor. Микропроцессор. Центральный процессор в виде одной микросхемы. Центральный процессор микроЭВМ.
Mixed-mode arithmetic. "Смешанная” арифметика. Арифметические операции, которые используют операнды как двойной,
так и одинарной длины или выдают результат другой длины, чем операнды. Операторы смешанного типа используются для
того, чтобы избежать арифметического переполнения.
Mixed-mode operator. Оператор смешанного типа. Арифметический оператор, который работает со ’’смешанной" арифмети¬
кой.
Mnemonic. Мнемоника. Техника помощи человеческой памяти, более конкретно-читаемое сокращение, состоящее из первых
букв или частей последовательности слов. В терминологии вычислительной техники — последовательность букв, которая об¬
разует инструкцию, используемую ассемблером для компиляции одной машинной команды. Мнемоники в Форт-ассемблере
— это слова, которые компилируют машинную программу.
Modulus. Мдцуль. Остаток целочисленного деления.
Most significant bit. Старший бит. Бит любого числа, который при двоичном представлении обозначает самую старшую циф¬
ру. В двоичном числе 101 самая левая единица является старшим битом.
Most significant byte. Старший байт. Байт любого числа, который при шестнадцатеричном представлении является старшим.
В шестнадцатеричном числе 12AF 12 является старшим байтом.
Most significant digit. Старшая цифра. Цифра любого числа, которая отображает наиболее значимую величину. В десяти¬
чном представлении 1 в 1295 является старшей цифрой.
Most significant number. Старшее число. В числе двойной длины число одинарной длины, в котором записана старшая часть
двойного числа. В шестнадцатеричном числе A34B9C5D A34B является старшим числом.
Name field. Поле имени. Часть откомпилированного Форт-описания в словаре, которая содержит имя слова и другую ин¬
формацию. См. также адрес поля имени.
Name-field address. Адрес поля имени. Адрес первого байта поля имени откомпилированного Форт-описания. Адрес поля
имени — это не адрес, хранящийся в поле имени.
NFA — сокращение для name-field address (адрес поля имени)
Nibble. Полубайт 4 бита.
Number. Число. В Форте — 16-битовое число, т.е. целое число одинарной длины или в зависимости от контекста любое
числовое значение.
Number base. Основание числа. Основание системы счисления. Число символов, первый из которых 0, используемое для
представления цифры в числе. В шестнадцатеричных числах (числах с основанием 16) диапазон символов простирается от 0
до 9 и от А до F, в то время как в восьмеричной (основание 8) — от 0 до 7. В Форте основание системы счисления при вво¬
де и выводе может изменяться от 2 до 70 путем изменения содержимого переменной BASE.
Object code. Объектный код. Исполняемая программа, обычно синоним машинной программы, полученной в результате
трансляции исходного текста посредством компилятора или ассемблера. Объектный код, полученный в результате компиля¬
ции Форт-ассемблером, является исполняемой машинной программой. Другие компилированные Форт-программы представ¬
ляют собой список адресов и(или) чисел, исполняемых только интерпретатором адресов Форта, и, хотя это не объектный код
в строгом смысле этого слова, его иногда называют объектным кодом Форта.
Octal notation. Восьмеричное представление. Представление чисел при основании системы счисления 8. Восьмеричные
цифры лежат в диапазоне 0 — 7. Каждая восьмеричная цифра представляется тремя битами. Восьмеричное представление
бывает полезным для понимания структуры машинных инструкций процессора.
One-dimentional array. Одномерный массив. Синоним вектора или линейного массива.
Ones complement. Дополнение по модулю один. Дополнение числа по модулю один получается путем инверсии всех битов
числа, т.е. все биты равные 1 сбрасываются в 0, а все нулевые биты делаются равными 1. Дополнение по модулю один числа
10011001 равно 01100110. См. также дополнение по модулю два.
Op-code Operand.- жаргонное выражение для operation code (код операции).
Operand.OnepaHA. Объект, с которым работает оператор. В Форте в выражении
3 4 +
277
3 и 4 являются операндами, используемыми оператором +.
Operation system. Операционная система. См. дисковая операционная система.
Operation code. Код операции. Машинная инструкция, иногда называется КОП.
Operator. Оператор. Символ или слово Форта, которые выполняют математическую операцию. В Форт выражении
3 4 +
+ является оператором, а 3 и 4 — операндами.
Outer interpreter. Внешний интерпретатор. Часть Форт-системы, которая интерпретирует входной поток, исполняя слова и
числа или компилируя их в описания новых слов или, менее часто, производя разбор входных данных. Называется также
текстовым интерпретатором. Противостоит интерпретатору адресов; внутреннему интерпретатору.
Overflow (data). Переполнение Сданных). Ошибка, когда результат операции превосходит по размеру место, выделенное для
его записи. Например, результат операции над числами одинарной длины дает переполнение, если он требует для своей за¬
писи более 16 битов. См. также арифметическое переполнение.
Overflow (stack). Переполнение стека. Переполнение стека происходит, когда место в памяти, выделенное для него, оказы¬
вается полностью использовано. В Форте это обычно случается, когда указатель стека параметров начинает указывать на
верх словаря.
Parameter-field. Поле параметров. Часть откомпилированного Форт-описания, которое содержит информацию, определяю¬
щую специфическое поведение слова. Поле параметров слова-двоеточие содержит список адресов полей программы слов в
описании. Поле параметров переменной содержит значение этой переменной. Поле параметров слова-примитива содержит
машинную программу, исполняемую этим примитивом.
Parameter field address. Адрес поля параметров. Адрес начала поля параметров в Форт-описании. Но не адрес, содержа¬
щийся в поле параметров.
Parameter stack. Стек параметров. Стек типа LIFO, с которым манипулируют Форт-программы. Называется также стеком
данных. Противостоит стеку возвратов.
Parsing. Разбор. Процесс разбиения входного потока Форта на алфавитно-цифровые последовательности, разделенные сим¬
волами-разделителями. Внешний интерпретатор Форта обычно производит выделение чисел или слов Форта в виде лексем,
используя пробел в качестве разделителя. Строки данных могут также подвергаться разбору, в каждом случае могут исполь¬
зоваться разные разделители. Разбор обычно производится словом WORD. Вообще в вычислительной технике — это прием
исходного текста программы и подготовка его для интерпретации или компиляции.
PFA. Обозначение адреса поля параметров.
Polish notation. Польская нотация. См. префиксная нотация.
Postfix notation. Постфиксная нотация. Форма математической нотации, в которой операторы следуют за операндами. Ал¬
гебраическое выражение
(2+3) * (4+5)
в постфиксной нотации должно выглядеть как
2 3 + 4 5 + *
Форт использует постфиксную нотацию, так как ее легче совместить с LIFO-стеком. Синоним инверсной польской нотации.
Противоположно алгебраической нотации префиксной нотации; инфиксной нотации.
Precedence bit. Лидирующий бит. Бит поля имени слова Форта, который определяет, является ли оно словом немедленного
исполнения и, следовательно, должно ли слово исполняться в режиме компиляции. См. также ’’слово немедленного испол¬
нения”.
Prefix notation. Префиксная нотация. Форма математической нотации, при которой оператор предшествует своим операн¬
дам. Алгебраическое выражение
(3+4) * (5+6)
в префиксной нотации должно выглядеть как
* + 34 + 56
To же, что и польская нотация. Противоположно алгебраической нотации; инфиксной нотации; постфиксной нотации.
Primitive. Примитив. Тип слов Форта, которые непосредственно исполняют машинную программу, в частности слова из ядра
Форта. Содержимое поля программы примитива указывает на поле параметров этого же слова, которое содержит исполняе¬
мую машинную программу.
Processor. Процессор. Центральный процессор или микропроцессор.
PUSH. Операция занесения числа в стек (см. также POP)
Radix. Основание системы считывания (см. также BASE)
RAM. Память с произвольным доступом (ППД)
Random access memory. Память с произвольным доступом. Главная (полупроводниковая) память, которая позволяет цент¬
ральному процессору как чтение, так и запись. Сокращение ППД. Противоположна постоянной памяти. Технически па¬
мятью с произвольным доступом может быть любая память, к которой может обратиться центральный процессор, но обычно
терминологически ПЗУ исключается. См. также память.
278
Rational approximation. Рациональная аппроксимация. Аппроксимация иррациональных чисел с помощью отношений це¬
лых чисел. Так, Пи (3.1415926...) может быть аппроксимировано с точностью трех десятичных знаков отношением 22/7
(3.1428571...), в то время как 355/113 (3.1415929...) является аппроксимацией Пи с точностью до семи десятичных знаков.
Read-only memory. Постоянная память. Основная (полупроводниковая) память, которая может быть прочитана, но содержи¬
мое которой не может быть изменено центральным процессором. Сокращение — ПЗУ. Противоположна памяти с произ¬
вольным доступом. См. также память.
Record (file). Запись (в файле). Группа связанных элементов информации, которые могут рассматриваться в файле как
единое целое. Например, типовой файл платежной ведомости должен содержать набор записей, включающих имя, зарплату
и другие необходимые данные, относящиеся к конкретному сотруднику. Место, выделенное для каждого элемента информа¬
ции в записи, называется полем.
Recursion. Рекурсия. Способность программы обращаться к самой себе. Рекурсия в Форте заключается в том, чтобы слово
могло обратиться к самому себе из своего описания, что может быть сделано с помощью слов MYSELF или RECURSE или
имени самого слова, в зависимости от версии.
Register. Регистр. Одна из нескольких ячеек памяти, используемых центральным процессором для различных целей.
Required word. Слово из обязательного списка. Слово Форта, которое должно присутствовать в стандартах Форт-79, или в
Форт-83, или в обоих. Обязательные слова образуют ядро стандартной системы Форта и должны использоваться в описании
всех слов, которые можно назвать "стандартными”. To же самое, что и стандартное слово. Противостоит словам из конт¬
ролируемого списка; словам из неконтролируемого списка; расширяющему набору слов.
Return stack. Стек возвратов. LIFO-стек Форта, который используется для хранения адресов слов, ожидающих завершения
исполнения, и извлечения их в процессе исполнения Форт-программы. Стек возвратов обычно скрыт от программиста, но он
может быть использован для временного хранения данных с помощью слов >R, R@ и R>. Стек возвратов используется также
обычно для хранения индекса и предела в do-loop.
Reverse Polish notation. Инверсная польская нотация. Синоним постфиксной нотации. Названа в честь Яна Лукашевича,
польского математика. Противостоит алгебраической нотации; инфиксной нотации; префиксной нотации.
Run-time code. Исполнительная программа. Машинная программа, на которую указывает адрес, содержащийся в поле про¬
граммы слова Форта. Исполнительная программа определяет, как будет исполняться слово Форта. Каждое слово-описатель
(такое как :, CONSTANT и VARIABLE) кладет адрес исполнительной программы, специфической для этого слова-описате¬
ля, во все производные слова. Исполнительная программа примитива — это машинная программа, лежащая в его поле пара¬
метров.
Scaling. Масштабирование. Процесс преобразования одного числа в другое с использованием фиксированного коэффициента
(масштабного коэффициента). Обычно это делается для того, чтобы не потерять точность или не допустить переполнения.
Преобразование 12.34 в целое 1234 является примером масштабирования также капреобразования миль в футы. Масштаби¬
рование особенно важно, когда используются целые числа, представляющие величины, обычно характеризуемые числами с
плавающей запятой. Выбор правильного коэффициента крайне важен для того, чтобы не потерять точность при арифметиче¬
ских расчетах. См. также масштабный коэффициент.
Scaling operator. Масштабный оператор. Одно из слов Форта, таких как */ и */MOD, которые используются для масштаби¬
рования чисел. Масштабные операторы Форта, чтобы сохранить точность, используют для промежуточных результатов числа
двойной длины.
Screen. Экран. Наиболее распространенный, в том числе и в этой книге, синоним выражения ”блок Форта, который может
быть отображен на терминале”, хотя значение и несколько варьируется. Чаще всего используется в отношении исходного
текста программы. Также, конечно, дисплей видеотерминала.
Search path. Путь поиска. Последовательность, в соответствии с которой производится просмотр одного или нескольких кон¬
текстных словарей. Метод просмотра варьируется от версии к версии очень широко.
Shadow block. Теневой блок. Блок комментариев и пояснений, который составляет пару с блоком, где размещен текст ис¬
ходной программы.
Signed number. Число со знаком. Целое число, в котором старший бит определяет его знак. Число представляется в виде до¬
полнения по модулю два. Если старший бит равен 1, число отрицательно.
Single-length number. Число одинарной длины. 16-битовое число со знаком или без. См. также 16-битовое число.
Smudge bit. Бит-метка. Во многих версиях Форта этот бит определяет, может ли быть данное число найдено в словаре. Бит-
метка используется, чтобы предотвратить обнаружение в словаре слов, которые не скомпилированы должным образом, хотя
это и плохая практика, так как приводит к тому, что ошибки занимают место в памяти.
Source code. Исходный текст программы. В любом языке ЭВМ — это исходный текст программы, который последовательно
транслируется в форму (машинного, объектного кода), которапая может быть непосредственно исполнена ЭВМ. Исходный
текст Форта состоит из описаний слов, которые обычно записаны в блоках или экранах. См. также язык высокого уровня;
язык низкого уровня; ассемблер; машинная программа, объектный код.
Stack. Стек. Последовательность ячеек памяти, используемая для временного хранения чисел. LIFO-стек (так как это сдела¬
но в Форте) определяет последовательность, в которой одни команды заносят числа в стек, а другие извлекают их из стека.
Стек управляется указателем стека, который указывает на верх стека. При записи и извлечении чисел указатель изменяется
279
таким образом, что он указывает на число, которое доступно в данный момент. Стек является очень эффективным способом
запоминания и извлечения обрабатываемой информации. Стек параметров (данных) используется для пересылки чисел меж¬
ду словами. В Форте имеется множество слов для манипулирования последовательностями чисел в стеке параметров. Отдель¬
ный стек (возвратов) контролирует исполнение слов и, как правило, циклов do-loop, хотя он может хранить и ограниченное
число кодов. Центральный процессор также использует стек параметров как временную память при выполнении машинных
программ. См. также LIFO-стек; стек параметров; стек возвратов.
Stack chart. Отображение стека. В Форте принято отображать содержимое стека параметров до начала и после завершения
исполнения слова Форта. Изменение в содержимое стека, вносимое словом, отображается слева направо, самое правое число
лежит на верху стека. Исполнение слова отмечается ”~” или ”->”. Пример отображения стека для слова SWAP:
(nl n2 — n2 nl) или (nl n2 -> n2 nl)
Stack-manipulation word. Слово, манипулирующее стеком. Слово Форта, которое изменяет порядок кодов или их число в
стеке. Примерами слов, манипулирующих стеком, являются DROP, DUP, SWAP, OVER, ROT, PICK и ROLL.
Standard word. Стандартное слово. Синоним выражения "слово из обязательного списка” (в стандартах Форта).
State-smart word. Слово, зависящее от STATE. Слово Форта, работающее по разному в зависимости от значения STATE,
которое определяет, находится ли Форт в режиме исполнения или компиляции. Примерами таких слов в Форт-79 являются
.” и ’.В Форт-83 отсутствуют слова из обязательного списка, зависящие от STATE.
String. Строка. Последовательность алфавитно-цифровых символов. Строки используются для записи ASCII-текстов. См.
также: счетная строка; сепаратор; ASCII.
Structured programming. Структурное программирование. Философия программирования, при которой каждая последова¬
тельность команд в программе представляет собой модуль только с одной точкой входа и выхода и которая по завершении
возвращает управление в точку, откуда произошел вызов. Программы должны писаться для того, чтобы они были исполнены,
с единственным исключением, когда условный оператор может выбрать один из альтернативных маршрутов исполнения,' ус¬
ловие может определить и время завершения цикла. Структурное программирование препятствует созданию текстов про¬
грамм, которые трудно понять, модифицировать и исправлять. Форт сам обеспечивает структурное программирование, но без
ограничений, присущих многим другим языкам, таким как Паскаль.
Stub. Подставка. Слово-подставка, описанное в Форте, не используется в окончательной версии программы, но оно опреде¬
ляется временно для того, чтобы проверить функции, которые могут использоваться в главной программе.
Target compilation. Целевая компиляция. В Форте — процесс компиляции элементов словаря в область памяти, не совпада¬
ющую с верхом словаря. Целевая компиляция может использоваться для создания полной системы Форта, которая будет ра¬
ботать в произвольной области памяти или которая может быть перенесена в другую ЭВМ. Часто используется синоним ’’ме¬
такомпилятор”. См. также кросскомпилятор; метакомпилятор.
Text-input buffer. Текстовый входной буфер. Область памяти в системе Форта, которая зарезервирована для приема инфор¬
мации от клавиатуры. См. также буфер; входной поток.
Text interpreter. Текстовый интерпретатор. Синоним внешнего интерпретатора.
Thread. Цепочка. В Форте — последовательность слов и машинных программ-примитивов, которые исполняются, когда ис¬
полняется слово из входного потока. Иногда используется в смысле последовательности слов, просматриваемых при поиске в
словаре.
Threaded interpretive language. Цепной интерпретивный язык. Цепной интерпретивный язык подобен обычному интерпре-
тивному языку, в котором исходная программа может быть исполнена без формирования объектного модуля или программы,
обеспечивающей оперативное и удобное взаимодействие между оператором и ЭВМ. Но в отличие от других интерпретивных
языков группы имеющихся коротких машинных программ сцепляются вместе, чтобы обеспечить последовательность выпол¬
нения, что много быстрее, чем способы, используемые в большинстве интерпретивных языков. В Форте это делается, когда
слово или последовательность слов выполняется с пульта или другого входного устройства с последующим исполнением ма¬
шинных программ, специфицированных при описании этих слов. Но в отличие от других интерпретивных языков эти цепоч¬
ки описаны в процессе написания программы, т.е., когда слово Форта описано, оно компилирует машинные программы,
подлежащие исполнению, задавая тем самым цепочку выполнения данного слова. Программы на цепном интерпретивном
языке исполняются фактически так же быстро, как откомпилированные программы, и обычно требуют меньше памяти. Форт
— наиболее популярный цепной интерпретивный язык. См. также компиляция; цепочка.
Token. Лексема. В Форте группа символов, которая выделена из входного потока внешним интерпретатором, и интерпрети¬
руемая как число или имя слова. См. также разбор.
Translation table. Таблица преобразования. Линейный массив, используемый для преобразования одного числа в другое.
Обычно число — это индекс массива, который при обращении заменяется элементом массива. Примером могла бы служить
таблица преобразования ASCII-символов в другие коды.
True (flag). Истинно (флаг). Число, полученное в результате проверки условия. В Форт-79 истинно соответствует 1, а в
Форт-83 1 (все 16 битов равны 1). См. также булев флаг; флаг ложно.
Tuos-complement notation. Представление в виде дополнения по модулю два. Метод представления отрицательных чисел
путем взятия дополнения по модулю 1 от их абсолютной величины и добавления к нему 1. Это дает тот же результат, что и
280
вычитание абсолютной величины из нуля. Дополнение по модулю два упрощает некоторые арифметические операции в дво¬
ичной арифметике. См. также дополнение по модулю один; числа со знаком.
Uncontrolled reference word. Слово из неконтролируемого списка. Слово, специфированное в стандарте Форт-83, которое
имеет уже определенную функцию в других версиях. Изменение функции не рекомендуется, но не запрещается. См. также
слово из обязательного списка; слово из контролируемого списка.
Unsigned number. Число без знака. Число, где все биты используются для кодировки двоичного кода. Противоположно чис¬
лу со знаком, где старший бит равен 1, если число отрицательно. См. также дополнение по модулю 2.
User slack. Стек пользователя. Синоним стека параметров.
User variable. Переменная пользователя. Форт-переменная, которая является частью базовой Форт-системы. Хотя она ис¬
пользуется подобно остальным переменным, она отличается от них способом запоминания. В описании такой переменной
хранится не ее величина, а указание на адрес. Переменные пользователя в многопользовательских версиях при идентичных
именах имеют разные значения для разных пользователей. Примерами таких слов являются BASE, STATE, CURRENT и
CONTEXT.
Vector. Вектор. Синоним линейного или одномерного массива.
Vectored execution. Векторное исполнение. Если вектор (линейный массив) содержит список адресов полей программы
слов, тогда эти слова могут быть исполнены путем занесения соответствующего элемента массива в стек и использования
EXECUTE. В Форте этот термин относится также к возможности изменить работу слова путем изменения содержимого пер¬
вого элемента поля параметров, заменяя его на CFA нового слова. Так, если EMIT описан как
: EMIT <EMIT> ;
и существует другое слово <NEWEMIT>, тогда EMIT можно заставить выполнять другую функцию, выполнив
FIND <NEWEMIT> ' EMIT !
в Форт-79 или
' <NEWEMIT> ’ EMIT >BODY !
в Форт-83.
Virtual memory. Виртуальная память. Массовая память (обычно диск), которая с помощью некоторой схемы может быть пе¬
реведена в состояние, когда она может рассматриваться как часть основной памяти. Техника перемещения блоков в буфер и
обратно и последующая адресация к буферам являются простой формой виртуальной памяти в Форте. См. также дисковый
кэш.
Vocabulary. Контекстный словарь. Связанный список — из словаря Форта. Контекстные словари используются для управле¬
ния порядком поиска слов Форта внешним интерпретатором. Применение различных контекстных словарей позволяет ис¬
пользовать одно и то же имя в различных контекстах без конфликта.
Word (Forth usage). Слово (использование в Форте). Фундаментальный элемент программирования в Форте и статья в
словаре. Слово Форта обычно содержит имя, связь с другим словом в словаре, указатель машинной программы, которая оп¬
ределяет, как будет выполняться слово, и один или более параметров (чисел, машинную программу, или список адресов дру¬
гих Форт-слов). Слова Форта вызываются по имени и аналогичны подпрограммам в других языках. Форт-программирование
состоит в описании новых слов через ранее описанные или с помощью машинных программ. Это определение слова отлича¬
ется терминологически от того, что принято в вычислительной технике, где слова — это последовательность битов, мини¬
мальное число битов в памяти, к которому возможна адресация со стороны центрального процессора. См. также словарь;
поле имени; поле связи; поле программы; поле параметров; длина слова.
Word length. Длина слова. В вычислительной технике минимальное число битов, к которому может непосредственно обра¬
щаться центральный процессор. Длина слова для большинства микропроцессоров равна 8-битовому байту, хотя используются
и 16-битовые слова; длина слова в базовой ЭВМ может быть равна 64 битам и более. Противостоит значению слова Форта.
ПРИЛОЖЕНИЕ В
ИСТОЧНИКИ ИНФОРМАЦИИ
Список систем Форта, публикаций и организаций не является исчерпывающим, такой задачи и не ставилось. Скорее, это
список источников, с которыми авторы знакомы и которые они сочли полезными.
Системы Форта
- Ниже рассмотрим системы Форта, которые предоставляют по крайней мере минимум средств для разработки программ.
Мы не включили в перечень версии Форта, о которых мы не смогли получить достаточно информации для оценки, мы не
ввели в список некоторые реализации Форта, которые не сочли отвечающими высоким требованиям. Но мы, вероятно, пре¬
небрегли некоторыми версиями Форта, достойными упоминания. Если мы не упомянулй вашего любимца, пожалуйста, про¬
стите нас.
Информация о конкретной системе может быть также неполной. Например, мы полагаем, что к моменту выхода книги по
крайней мере две из нижеупомянутых реализаций Форта будут, вероятно, иметь версии, работающие на других ЭВМ. И мы
упомянули только те улучшения, которые ”выше нормьГ. Мы предполагаем, любая нормальная версия Форта имеет редак¬
тор, ассемблер, библиотеку для работы с числами двойной длины и т.д., хотя эти средства и не являются обязательными в
соответствии со стандартами. Большинство версий имеют также много полезных средств, которые мы не упомянули, потому
что они специализированные, или в силу того, что их нельзя описать достаточно кратко. Таким образом, мы не упомянули
об использовании в MMSFORTH операторов QUAN, массивов, условных структур и других сходных программных средств.
Аналогично мы не описали слово из F83 VIEW, которое позволяет сразу найти блок с исходным текстом описаний нужного
слова, сегментную структуру словаря HS/FORTH и расширенную версию MVPFORTH, комплексную и очень мощную систе¬
му разработки программ. Выбор подходящей версии Форт-системы может оказаться обескураживающей задачей, современ¬
ные системы Форта имеют очень широкий спектр возможностей, и то, что является идеальной системой для одного задания,
может оказаться менее удобным для другого. Нужно полагать, что на исследования при выборе системы будет затрачено зна¬
чительное время. К сожалению, в журналах не публикуются обзоры по системам Форта, как, скажем, по компиляторам Па¬
скаля. Вам следует изучить литературу и обсудить ваши нужды с несколькими поставщиками. Если поставщик не будет об¬
суждать ваши проблемы, забудьте об этом, вы, вероятно не получите поддержки при решении ваших проблем после покуп¬
ки. В некоторых случаях вы можете купить ’’голую” версию языка и расширять систему по мере необходимости, а в других
случаях вы сможете купить копию документации, чтобы выяснить, имеет ли система то, что вам надо. Наконец, вы должны
знать, что цена — не всегда мера ценности для Форта. F83 за 25 долл. или даже бесплатно в свободном доступе имеет неко¬
торые возможности, которых нет в некоторых Форт-системах ценой более 100 долл. Мы не упомянули одну систему с ценой
более 200 долл., так как нам кажется, что она не соответствует своей цене. В то время как наиболее продвинутая система
polyFORTH II стоимостью несколько тысяч долларов предлагает фантастические возможности, потребности большинства про¬
граммистов можно удовлетворить много более дешевой версией.
Рекомендуется просмотреть следующий список в качестве введения, чтобы помочь вам получить больше информации.
FORTH, lnc.
2309 Pacific Coast Highway
Herittosa Beach, CA 90254
(213) 372-8493
polyFORTH II. Нестандартный, но близок к Форт-79.
Оборудование: слишком широкий список микро- и мини-ЭВМ, чтобы приводить его целиком. Продается по различным
ценам с различным диапазоном возможностей и сервиса для пользователя.
Особенности: очень широкий спектр возможностей, лишь немногие здесь описаны. Работает с ДОС или автономно, име¬
ются многозадачный и многопользовательский режимы; поддержка баз данных; интерактивная графика; плавающая арифме¬
тика 8087.
Замечания: это ррямой потомок Форта Мура, разработанный компанией, которая была им основана. Хотя polyFORTH не
является стандартным и в некотором смысле консервативным, не имеющим некоторых ’’фантастических” черт других вер¬
сий, он, пожалуй, представляет собой наиболее профессиональную систему и используется'для более сложных и изощрен¬
ных задач, чем какая-то другая версия.
FORTH Interest Group (FIO)
P.O.BOX 8231
San Jose, CA 95155
(408 ) 277-0668
FIGFORTH. Нестандартный, но фактически сам по себе стандарт.
Оборудование/ХШ, 6502, 6800, 6809, 68000, 8080, 8086/8, 9900, Alfa Micro, Apple II, Eclipse, IBM PC, Nova, Расе, PDP-
11, VAX, Z80. Для всех этих ЭВМ имеются тексты ассемблеров. Различные рабочие версии имеются в свободном доступе.
Особенности:• никакие усовершенствования FIG не поставляет. Существует множество усовершенствований в свободном
доступе и много опубликовано в периодике FIG, ”FORTH Dimensions”.
Замечания: это, пожалуй, самая распространенная Форт-система, на которой базируются многие коммерческие версии.
Несмотря на распространение, он вряд ли пригоден для серьезного применения. FIGTORTH стал де-факто стандартом и усо¬
вершенствован многими пользователями.
282
Harvard Softworks
P.O.Box 2579
Springfield, OH 45501
(513) 748-0390
HS/FORTH. Комбинация Форт-79 и Форт-83. Работает под операционной системой.
Оборудование: IBM PC и совместимые с ней.
Особенности: Исходные тексты в безблочных ДОС-файлах (возможна работа с файлами); средства отслеживания; быст¬
родействующий оптимизатор (быстродействие близкое к машинному); графика; звуковое сопровождение; слова без заголов¬
ков; непосредственное программирование в машинных кодах; плавающая арифметика 8087; неограниченная возможность ис¬
пользования расширенной памяти.
Замечания: HS/FORTH очень необычно (но эффективно) использует память, имеет необычную структуру словаря и неко¬
торые другие уникальные черты.
Laboratory Microsystems Inc.
P.O.Box 19430
Marina del Rey, CA 90295
(213) 306-7412
Следующие версии являются 16-битовыми:
68000 FORTH, 8080 FORTH, 8086 FORTH, Z80 FORTH, PC/FORTH.
Следующие версии являются 32-битовыми:
68000 FORTH+, 8086 FORTH+, PC/FORTH+.
Все работают под ДОС и отвечают требованиям стандарта Форт-83.
Оборудование: 68000, 8080, 8086/8, Z-80, СР/М, СР/М-86, CP/M-68K, MS-DOS, IMB PC и совместимые.
Особенности: отладчик; возможность перекрестных ссылок; использование расширенной памяти; целевой компилятор;
средства для работы с файлами; программы для плавающей арифметики; поддержка AMD 9511; графика; любые наборы
символов; телекоммуникация; оверлеи словаря.
Microniotion
12077 Wilshire Boulevard
Los Angeles, CA 90025
(213) 821-4340
MasterFORTH Форт-83. Работает под ДОС.
Оборудование: Macintosh, IBM PC и совместимые, IBM PCjr, APPLE II, серии-СР/М, Commodore 64.
Особенности: отладчик; декомпилятор; работа с файлами; описания без заголовков; перемещаемая область компиляции;
графика; программы для плавающей арифметики; система перемещения модулей.
Miller Microcomputer Services
61 Lake Shore Road
Natick, MA 01760
(617) 653^>I36
MMSFORTH Форт-79. Автономная версия. Возможна работа и с ДОС.
Оборудование: TRS-80 модели I, III, 4; IBM PC и совместимые.
Особенности: строки; графика; программы для работы с числами с плавающей точкой; арифметика с плавающей точкой
8087; возможность перекрестных ссылок; средства поиска; слова без заголовков; перемещаемая компиляция; адресация к
расширенной памяти; произвольный доступ к диску; дисковые форматы по выбору пользователя; буферизатор вывода на
принтер; поддержка баз данных; экспертные системы; система управления базами данных; процессор слов; возможность по¬
следовательной передачи данных.
Mountain View Press, lnc.
P.O.Box 4656
Mountain View, CA 94040
(415) 961-4103
MVP-FORTH Форт-79. Версии для ДОС и автономного использования. Основная система в свободном доступе.
Оборудование: СР/М, СР/М-86, MS-DOS, Apple II серия, Macintosh, IBM PC и совместимые, Commodore 64, NEC 8201,
TRS-80/100, HP 110, HP 150.
Особенности: не все доступны для всех реализаций. Кросскомпилятор; целевой компилятор; программа переноса; отлад¬
чик; средства отслеживания; декомпилятор; поддержка расширенной памяти; MS-DOS файл-интерфейс в автономном режи¬
ме; описания без заголовков; графика; программы для операций над числами с плавающей точкой; арифметика с плаваю¬
щей точкой 8087; плавающая арифметика 9511; окна; поддержка системного ПЗУ; экспертная система; процессор слов.
Замечания: базовый MVP-FORTH находится в свободном доступе и может быть получен через одну из многих групп поль¬
зователей. Фирма Mountain View Press является поставщиком других коммерческих Форт-систем, включая некоторые не упо¬
мянутые здесь. Они являются хорошим источником информации о Форт-версиях, доступных на различных ЭВМ.
Next Generation Systems
P.O. Box 2987
Santa C1ara, CA 95055
(408) 241-5909
NGS FORTH Форт-79. Работает под ДОС.
Оборудование: IBM PC и совместимые.
Особенности: режим работы сходен с FIG-FORTH; адресация к расширенной памяти, отладчик; средство отслеживания;
декомпилятор; графика.
No Visible Support Software
Box 1344
2000 Center Street
Berkeley, CA 94704
F83 Форт-83. Работает под ДОС.
Оборудование: 8080 СР/М, 8086 СР/М, 68000 СР/М (все на 8-дюймовом диске), IBM PC и совместимые (5-дюймовый
диск).
Особенности: отладчик; декомпилятор; метакомпилятор; многозадачный режим; строки; теневые блоки.
Замечания: свободный доступ; от пользователей можно получить различныереализации в разных дисковых форматах.
Хотя и находится в свободном доступе, он является намного более полным, чем FIGFORTH или базовый пакет MvPFORTH
(впрочем, он не столь всеобъемлющ, как "развитый” MVPFORTH, который доступен от Mountain View Press). ”No Visible
283
Support Software” (” Программа без какой-либо видимой поддержки”) — как раз то, что подразумевается именем. Здесь нет
какой-либо поддержки пользователя или документации, кроме того, что имеется в коротком текстовом файле и в теневых
блоках на диске. Теневые блоки, однако, весьма исчерпывающи.
Parsec Research
Drawer 1776
Fremont, CA 94538
Dealer, Mountain View Press
SuperFORTH 64+AI Форт-79. Работает под ДОС.
Оборудование: Commodore 64.
Особенности: арифметика с плавающей точкой; графика; строки; отладчик; средство отслеживания; декомпилятор; гене¬
рация программ, загружаемых в ПЗУ.
Quest Research lnc.
P.O.Box 2553
Huntsville, AL 35804
(204) 539-8086
FORTH-32 Форт-79. Работает под ДОС.
Оборудование: IBM PC и совместимые.
Особенности; декомпилятор; отладчик; графика; программа перемещения; полномасштабное применение 32-разрядного
адресного пространства памяти; арифметика с плавающей точкой 8087; целевая компиляция.
Shaw Laboratories, Ltd.
24301 Southland Drive, #216
Hayward, CA 94545
(415) 276-5953
TaskFORTH Форт-79; Форт-83 с перекрытием. Работает под ДОС.
Оборудование: СР/М в различных форматах, MS-DOS.
ОсоЬенности: многозадачный и многопользовательский режимы; отладчик; средства отслеживания; декомпилятор; управ¬
ление базами данных; иерархическая система файлов; строки; улучшенное управление дисплеем и принтером; целевой ком¬
пилятор; кросскомпилятор; адресация к расширенной памяти.
Ubiquitous Systetiis
13333 Belevue-Redmond Road, N.E.
Bellevue, WA 98005
(206) 641-8030
u4th Форт-83 (с ограничениями). Работает под ДОС.
Оборудование: различные UNIX и XENIX системы. Plexus, IBM-XT/AT, Intel 286/310, SUN, Tendy 6000, TRS-80 16B,
VAX.
Особенности: написано на Си, новые примитивы можно написать на Си; целевая компиляция; передача команд в систе¬
му UNIX; компиляция в Си-коды; поддержка, где нужно, 32-разрядной адресации; строки; работа в верхнем и нижнем реги¬
страх; компиляция файлов и блоков; динамическое перераспределение памяти; исходные тексты для дополнений, ориентиро¬
ванных на объектные коды.
WL Computer Systems
1910 Newman Road
West Lafayette, IN 47906
(317) 494-2564
WL FORTH Форт-79. Работает под ДОС.
Оборудование: IBM PC и совместимые.
Особенности: арифметика с плавающей точкой 8087.
Публикации
Мы полагаем, что мы включили все основные книги и периодические издания, которые были к моменту написания книги
и которые посвящены Форту. Мы также включили публикации, которые сочли полезными в качестве справочных пособий
при работе с Фортом или которые включают материалы по Форту. Многие из материалов можно получить от FORTH Interest
Group (Группа пользователей Форта), фирм Miller Microcomputer Services и Mountain View Press, а также от других постав¬
щиков Форт-систем (адреса приведены выше).
Книги
1. Anon. 1980. FORTH-79. Публикация группы по стандартам Форта. FORTH Interest Group, Sant Carlos, Calif. Тексты
стандартов для Форт-79.
2. Anon. 1983a. FORTH-83 Standard. Публикация группы по стандартам Форта. Mountain View Press, Mountain View, Calif.
Тексты стандартов для Форт-83.
3. Anon. 1983b. MVP-FORTH Source Listing. Mountain View Press, Mountain View, Calif. Исходный текст ассемблера для ба¬
зовых MVP-FORTH систем для 8080, IBM PC и Apple II.
4. Anon. 1980. [без заголовка] FORTH Interest Group, Sant Carlos, Calif. Собрание информационных перепечаток из жур¬
нала ”BYTE”, включая выпуск, посвященный языку Форт.
5. Anderson A., Tracy М. 1984. FORTH Tools, Vol. l.Micromotion, Los Angeles, Calif. Вводный текст, рассматривающий
только Форт-83. Не предполагает какого-либо знания ЭВМ. Хорошие упражнения и примеры. Не обсуждаются более слож¬
ные темы, такие как ассемблер, работа внешнего и внутреннего интерпретаторов и т.д., но в остальном довольно полное из¬
ложение предмета.
6. Brodie L. 1981. Starting FORTH. Prentice-Hall, Englewood Cliffs N.J. Введение в ПолиФорт с некоторыми упоминаниями
отличий от FIGFORTH и Форт-79. Не предполагает предварительного знания ЭВМ. Легко читается, стиль весьма живой.
284
Сложные темы рассмотрены не слишком подробно. Ограниченное число примеров и упражнений. Указатель неполный. Пер¬
вая достаточно общая книга по Форту и поэтому наиболее популярная. Переведена на русский язык.
7. Brodie L. 1984. Thinking FORTH. Prentice-Halli, Englewood Cliffs N.J. Философия и способ мышления при решении про¬
блем на Форте. Обсуждение составления программ со многими примерами, советами и программными процедурами. Способ-
ствуетразмышлению.
8. CassadyJJL 1981. MetaFORTH. Распространяется фирмами Mountain View Press, Mountain View, Calif. Исходные тексты
метакомпилятора (целевой и кросскомпилятор), написанные на FIGFORTH.
9. Chirlian Р. 1983. Beginning FORTH. Matrix. Beaverton, Oreg. Введение, которое не предполагает какого-либо знания в
области ЭВМ. Элементарный материал изложен хорошо, с примерами и упражнениями, но изложение тем промежуточного
уровня, таких как компиляция и интерпретация, а также слов типа IMMEDIATE, [COMPILE] и COMPILE, недостаточно. Не
обсуждаются темы высокого уровня, такие как структура словаря, работа Форта или ассемблера. Хороший указатель.
10. Derick М. and Baker L. 1982. FORTH Encyclopedia. Mountain View Press, Mountain View, Calif. Детальное описание всех
слов FIGFORTH с диаграммами. Очень полезно для понимания того, как работают слова Форта.
11. Edmunds R. 1985. The Prentice-Hall Standard Glossary of ComputerTerminology. Prentice-Hall, Englewood Cliffs N.J. Да¬
ются общие определения и обсуждается терминология вычислительной техники, включая многие малоизвестные термины.
Терминология Форта не рассмотрена.
12. Feierbach G., Thomas Р. 1985. FORTH Tools and Applications. Reston, Reston,Va. Краткое, но полезное обсуждение и
примеры разработки программ Форта, отладка и приемы программирования. Полезные программы.
13. Haydon G. 198o. All about FORTH. Mountain View Press, Montain View, Calif. Описание, обсуждение и примеры ис¬
пользования всех слов MVP-FORTH. Сравнение слов MVP-FORTH со словами Форт-79 и FIGFORTH. Очень полезно для по¬
нимания того, какработает язык.
14. Hofert D. 1985. А Bibliography of FORTH Referances, 2nd ed. The Institute for Applied FORTH Research, Inc., Rochester,
N.Y. Очень* полезный, практически полный список перекрестных ссьшок на когда-либо опубликованные статьи по Форту.
15. Huang Т. 1983. And So FORTH. Central Book Co., Taipei, Taiwan. Странное, но полезное и очаровательное собрание
материалов по истории Форта, примеры программ, приложения языка и т.д. Хотя рассматривается как учебник для коллед¬
жей, его изложение слишком фрагментарно для этих целей. Пригоден для опытных пользователей Форта.
16. Knecht К. 1982. Introduction to FORTH. Howard W. Sams, Indianapolis, Ind. Краткое введение, основанное на теперь
уже устаревшей и нестандартной версии MMSFORTH.
Полезные и интересные примеры, но мало или совсем нет материалов по темам промежуточного и высокого уровня сложно¬
сти.
17. Lipschutz S. 1982. Essential Computer Mathematics. Schaum’s Outline Series, McGraw-Hill, New York. Сжатый, но очень
полезный краткий курс практической математики в приложении к ЭВМ, где рассмотрены системы счисления, двоичная
арифметика, кодирование чисел и букв, булева алгебра, а также векторы и матрицы.
18. Loeliger R. 1981. Threaded Interpretive Languages. Byte Books, Peterborough, N.H. Таинственное, трудночитаемое, пере¬
груженное жаргоном, но очень ценное описание гшименения цепных интерпоетивных языков, в частности Форта.
19. McCabe C.K. 1983. FORTH Fundamentals. Dilithium Press, Beaverton, Oreg. Введение, предполагающее небольшое зна¬
ние ЭВМ, охватывает FIGFORTH и Форт-79. Изложение примерно на уровне книги Броуди с недостаточным изложением
продвинутых тем. Плохой указатель делает книгу непригодной для поиска дополнительных материалов.
20>. Morgan C., Waite м. 1982. 8086/8088 16-Bit Microprocessor Primer. Byte/McGraw-Hill, Peterborough, N.H. Превосход¬
ное изложение архитектуры и набора команд Intel 8088, которое может помочь пониманию нашего описания ассемблера, в
гл. 16.
21. Scanlon L. 1982. FORTH Programming. Howard W. Sams, Indianapolis, Ind. Введение в FIGFORTH и Форт-79, которое
предполагает некоторое знание вычислительной техники. Рассмотрены основы и некоторые темы промежуточного уровня,
совсем оставлены без внимания такие темы, как структура словаря, интерпретаторы или пропраммирование на ассемблере.
22. Ragsdale W. 1980 FIGFORTH Installation Manual. FORTH Interest Group, San Carlos, Calif. Исходные тексты
FIGFORTH. Очень полезна для понимания того, как работает Форт.
23. Reymann J. 1983. UnderstandineFORTH. Alfred Publishing Co., Sherman Oaks, Calif. Короткая брошюра, дающая пред¬
ставление об основных особенностях Форта для тех, кто не знаком с языком.
24. Stevens W. 1979. А FORTH Primer. Distributed by the FORTH Interest Group, San Carlos, Calif. Книга известна под на¬
званием "Kitt Peak Primer”, это описание Форта, история его создания Чарльзом Муром в Национальной обсерватории Kitt
Peak. Хотя оно и несколько устарело, в нем содержатся интересные мысли ирассмотрена перспективы развития языка.
25. Ting С. 1983. FORTH Notebook. Offete Enterprises, San Mateo, Calif. Собрание необычных и интересных программ на
Форте.
26. Winfield А. 1983. The Complete FORTH. Sigma/Wiley, New York. Введение, охватывающее Форт-79 вплоть до проме¬
жуточного уровня сложности. Прекрасные примеры и объяснения. Изложение более сложных тем, касающихся того, как ра¬
ботает Форт, ограниченно или отсутствует вовсе.
27. Zaks R. 1980. How to Program the Z80. SYBEX, Berkeley, Calif. Превосходное изложение архитектуры и набора команд
Z-80, которое поможет в понимании нашего описания ассемблера в гл. 16.
Периодика
Существуют два периодических издания, целиком посвященные языку Форт. "FORTH Dimensions" — журнал, выпускае¬
мый ежемесячно объединением FORTH Interest Group (FIG см. ниже). В нем содержится широкий круг публикаций — от
вводных учебных материалов до исходных текстов ассемблеров, метакомпиляторов и т.д. Здесь достигнут прекрасный комп¬
ромисс между материалами для начинающих и довольно серьезными статьями. Старые, весьма полезные номера можно по¬
лучить от FIG. Институт прикладных исследований Форта (см. ниже) ежеквартально выпускает профессиональный журнал
"The Journal of FORTH Applications and Research". Он включает в себя присылаемые статьи обычно высокого уровня по осно¬
вополагающим и теоретическим темам.
Существуют ежегодные публикации материалов профессиональных конференций. FORTH Interest Group публикует жур¬
нал FORML Conference Proceedings", где содержатся статьи, представленные на ежегодное собрание FORTH Modification
Laboratory. Статьи имеют тот же уровень, что и в "FORTH Dimensions". Материалы ежегодной Рочестерской конференции
по Форту публикуются институтом прикладных исследований по Форту. Хотя трудно точно оценить, статьи близки по уров¬
ню "Journal of FORTH Applications and Research".
Два универсальных журнала часто содержат материалы по Форту. Журналы "Dr. Dobbs Joumal" и "Computer Language"
дают некоторое число публикаций по Форту в большинстве номеров, и оба обещают публиковать ежегодно один номер, це¬
ликом посвященный Форту.
285
организации
Существуют две организации программистов на Форте. Их адреса:
FORTH Interest Croup (FIG)
P.O. Box 8231
San Jose, CA 95155
и
Thc Institute for Applied FORTH Research, Inc.
70 Elmwood Avenue
Rochester, NY l46ll
Их деятельность описана во введении. Короче говоря, общество FIG является, скорее, любительской организацией. Оно
ответственно за распространение FIGFORTH, публикует "FORTH Dimensions", организует встречи, такие как FORML, суще¬
ствуют отделения или группы любителей по всему миру. "The Institute" ставит целью повысить качество применения Форта.
Он организует Рочестерские конференции и публикует "Journal of FORTH Applications and Research”.
ПРИЛОЖЕНИЕ г
ASCII-КОДЫ
Управляющие коды и их значения в базовом телеграфном стандарте представлены ниже в таблице; альтернативные зна¬
чения даны в скобках. Немногие ЭВМ (если вообще такие существуют) или современные терминалы используют все эти зна¬
чения, и только некоторые распознают все комбинации "CTRL-клавиш", в осооенности коды ’’CTRL-клавиш” в верхнем ре¬
гистре. Несмотря на это, многие коды, такие как «BS», «перевод строки - LF», «ТАВ» и т.д., распознаются, поэтому это руко¬
водство будет полезным. Некоторые ЭВМ и терминалы используют также нестандартные символы для некоторых символьных
кодов, в частности для [ , \, ], ~, -, {, |, } и ".
Управляющие коды
Десятич.
Шестнадц.
ASCII
Примечания
000
000
NUL
Нуль, подача ленты, CTRL-SHIFT P в верхнем регистре
001
001
S0H
Начало заголовка [SOM, начало сообщения], CTRL-A
002
002
STX
Начало текста [EOA, конец адреса]; CTRL-B
003
003
ETX
Конец текста [EOM, конец сообщения]; CTRL-C
004
004
E0T
Конец передачи, отбой в системе телетайпной связи,
отбой для (TWX) телетайпов, отключение некоторых
наборов данных, CTRL-D
005
005
ENQ
Запрос [WRU, ”Кто вы”],переключательидентификации
("Здесь ”) на удаленной станции.если это предус
мотрено, CTRL-E
006
006
ACK
Подтверждение [RU,"Bbi...?"]; CTRL-F
007
007
BEL
Звуковой сигнал.СТРЬ-О
008
008
BS
Смещение курсоравлево, стирание символа
слева от курсора; CTRL-H
009
009
HT
Горизонтальная табуляция; CTRL-I
010
00A
LF
Перевод строки; CTRL-J
011
00В
VT
Вертикальная табуляция; CTRL-K
012
00C
FF
Переход на начало следующей страницы; CTRL-L
013
00D
CR
Возврат каретки на начало строки, CTRL-M
014
00E
S0
Выход из режима;смена шрифта или цвета ленты; CTRL-0
015
00F
SI
Вход в режим; возврат к стандартному
шрифту или цвету ленты; CTRL-N
016
010
DLE
Освобождение канала данных [DC0]; CTRL-P
017
011
DC1
Управление устройством 1; включение
перфосчитывателя,(Х вкл); CTRL-Q
018
012
DC2
Управление устройством 2, вклю
чение ленточногоперфоратора (Вспом вкл.); CTRL-R
019
013
DC3
Управление устройством 3;
выключение перфосчитывателя (X выкл.), CTRL-S
020
014
DC4
Управление устройством 4; выключение пер
форатора (Вспом. выкл.); CTRL-T
021
015
NAK
Негативное подтверждение [ERR, ошибка] CTRL-U
022
016
SYN
Пассивная синхронизация [SYNC]; CTRL-V
023
017
ETB
Конец передачи блока [LEM, логи
ческий конец носителя]; CTRL-W
024
018
CAN
Отмена [Sg]; СТР1-Х(Начало сообщения "можно")
025
019
ЕМ
Конец носителя [S^];CTRL-Y
Подстановка [S2];CTRL-Z
026
01A
SUB
027
01В
ESC
Префикс ESC [So]; CTRL-K в верхнем регистре
или клавиша ESC
028
01C
FS
Разделитель файлов; CTRL-L в верхнем регистре
029
01D
GS
Групповой разделитель, CTRL-M в верхнем регистре
030
01E
RS
Разделитель записей; CTRL-N в
верхнем регистре
031
01F
US
Разделитель элементов; CTRL-0 в верхнем регистре
286
Таблица кодов
Де-
сят
Шест-
над-
цатер
ASCII
Де-
сят
Шест-
над-
цатер
ASCII Де-
сят
Шест-
над-
цатер
ASCI
032
020
ПРОБЕЛ
064
040
@
096
060
’
033
021
I
065
041
А
097
061
а
034
022
"
066
042
В
098
062
b
035
023
tt
067
043
С
099
063
с
036
024
$
068
044
D
100
064
d
037
025
%
069
045
E
101
065
e
038
026
&
070
046
F
102
066
f
039
027
071
047
G
103
067
g
040
028
(
072
048
H
104
068
h
041
029
)
073
049
I
105
069
i
042
02A
*
074
04A
J
106
06A
j
043
02В
+
075
04В
К
107
06В
k
044
02C
076
04C
L
108
06C
l
045
02D
-
077
04D
M
109
. 06D
m
046
02E
078
04E
N
110
06E
n
047
02F
/
079
04F
0
111
06F
о
048
030
0
080
050
P
112
070
P
049
031
1
081
051
Q
113
071
Q
050
032
2
082
052
R
114
072
r
051
033
3
083
053
S
115
073
s
052
034
4
084
054
T
116
074
t
053
035
5
085
055
и
117
075
u
054
036
6
086
056
V
118
076
V
055
037
7
087
057
w
119
077
w
056
038
8
088
058
X
120
078
X
057
039
9
089
059
Y
121
079
У
058
03A
090
05A
z
122
07A
2
059
03В
;
091
05В
[
123
07В
i
060
03C
<
092
05C
\
124
07C
I
061
03D
=
093
05D
]
125
07D
>
062
03E
>
094
05E
126
07E
~
063
03F
?
095
05F
127
07F
DEL
ПРИЛОЖЕНИЕ Д
ОТВЕТЫ К УПРАЖНЕНИЯМ
Блок 0 [0 :0]
Блок 3 [3 :0]
Глава 1. Упражнения 1. 01 нз 02 )
1. )
а. 10 б. 0 в. 6 г. 1 д. 4 )
2. )
«. 1 2 » 6. 1 2 * 2 /
в. 2 3 / 1 I г. 1 2 + 3 /
д. 3 2 1 + / e. 3 4 + 2 1 t / )
( 3. )
: P0TER4 ( о - ) DUP CUBE * ;
( 4. )
: NEW0IER4 ( n - ) SQUARE SQUARE ;
( 5. )
: PYTHAGORUS ( nl n2 - ) SQUARE SVAP SQUARE + ;
Блок 1 [1 :0]
( Глава 1. Упражнения 1. 02 нз 02 )
6. )
AREA ( радиус - ) SQUAR8 314 • ;
( 7. )
VOLUME ( длнна радиус -- ) AREA • ;
XVOLUME ( радиус длина — ) SVAP AREA •
Второе описание менее эффективно из-за
липнего SVAP . )
Блок 2 [2 :0]
Глава 1. Упражненя 2. 01 из 02 )
1. ) ( Главное преимущество днска заключается в
уменыпении вероятности потери программы
и в простоте ее изменения.)
.X ." -" ; ( .1 может быть изменено для
отображения любого символа. )
: LIMITBAR ( n -) DUP 40 > IF DR0P 40 THEN BAR
( Для использования с 80-символьным экраном:
: LIMITBAR ( n - ) DUP 80 >IF DR0P 80 THEN BAR ;
Аналогичным образом можно приспособиться к экрану
любой ширины )
4- )
L1UITBAR1 ( n ~) 100 / DUP 64 > IF DR0P 64 THEN BAR
5.
LIMlTBAR2 ( о - ) 10000 / 64 < DUP 64 >
lF DR0P 64 THEN BAR ;
( 2. )
( 3. )
(
( М
Глава 1. Упражнения 2. 02 из 02 )
6. )
GRAPH ( nl n2 n3 ... -, или nl n2 n3 ) CR DEPTH DUP
16 < IF DO LIMITBAR L00P THEN ;
Заметим, что исходное число может остаться в стеке.)
7. )
BAR ( о ~ ) DUP 0 D0 .X L00P . CR ;
DUP позволяет использовать верхнее число как
LIMITBAR, так и . )
LIMITBAR3 ( о - ) DUP 50 > IF DR0P 50 THEN BAR ;
0
1
2
3
4
5
6 :
7 (
8
9 :
10
11
12
13
14
15
Блок 4 [4 :0]
5
Глава 2. Упражнения 1.
1. )
5 5 + 5 + .
5 5 • 5 •
5 5 + 5 • .
5 5 + 5 / .
5 5 + 2 / .
е. 10 5 5 + /
ж. 5 4 + 5 5 +
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Блок 5 [5 :0]
01 из 04 )
Ответ:
5 5
5 4
5 4
5 4
5 4
5 4
4 4
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Глава 2
2. )
i
б
Упражнения 1. 01* из 04 )
г.
Д.
е.
ж.
з.
и.
к.
5
5
5
Это
5 5 + + .
5 5 5 • • •
5 5 + • .
нельзя изменить.
Ответ:
и это нет.
Это тоже нельзя.
И зто нё может быть изменено
5 5 • 5 4 • 4 4 • + + .
Эти два также нельзя
изменить. )
15
625
50
2
5
1
0
61
81
81
15
625
50
61
288
Блок 6 [6 :0]
| Глава 2. Упражнения 1. 03 из 04 )
. 3. )
i Сначала сложим 3 и 5, умножим на 2, разделим на 4 и
добавим 16. Постфиксная запись: 3 5 + 2 • 4 / 16 +
( дает 20). ( Заметим, что если естъ выбор, то, чтобы
не было ошибки округления, умножение должно всегда
предаествовап делению. Например:
7 100 • 6 / выдает 116
в то время как
7 6 / 100 • выдает 100 )
( *• >
9
3
f
5 • 6 / 2 I 32 I .
Ответ: 44
5
6
+
3 • 3 • 2 • 32 • .
6336
5
4
+
22 • 5 10 • 2 + + 2
♦ . M0
Блок 7 [7
:0]
( Глава
2.
Упражнения 1. 04 из 04 )
5. )
( *
5
DUP DUP I I .
Ответ: 15
б.
5
DUP DUP DUP • • • .
625
в.
5
DUP DUP I • .
50
г.
5
DUP I 5 / .
2
д.
5
DUP + 2 / .
5
e.
10
5 DUP f / .
1
ж.
5
4 + 5 DUP + / .
0
3.
5
DUP • 5 4 • + 4 DUP
♦ I . 61
и.
5
4 + DUP • .
81
к.
5
4 + DUP • .
81
Блок 8 [8 :0]
i Глава 2. Упражнения 2. 01 из 04 )
1.
а.
)
SIAP
б. DUP
в.
OVER
г. OVER SfAP
Д.
DUP ROT
e. ROT
ж.
SIAP
з. SfAP ROT
и.
ROT SfAP
к. ROT ROT
л.
OVER SIAP
м. 3 PICK ROT ROT
2 PICK ROT ROT
F0RTH-79
F0RTH-83
н.
OVER SfAP DUP
0.
3 PICK ROT ROT OVER SfAP DUP
2 PICK ROT ROT OVER SfAP DUP
79
M
)
Блок 9 [9 :0]
0 ( Глава 2. Упражнения 2. 02 из 04 )
1 I 2 )
1 i с. )
2 ( а. •
б. DUP
3 в. SfAP DUP f f
г. -
4 д. DUP DUP • • SfAP DUP • • f. OVER I
i
5 e. + DUP • факторизуем [а + Ъ] [а + b]
6 ж. ROT ROT f SfAP /
7 з. OVER f ROT ROT f SfAP /
8 и. SfAP OVER f ROT ROT f SfAP /
9 к. DUP f ROT ROT f SfAP /
10 л. ROT ROT f SfAP DUP f /
11 м. OVER SfAP - ROT ROT SfAP - •
12 н. + + 1 факторизуем а [b + с I d
13 о. 1 1 I 1 4 факторизуем а b [1 f с d
)
14
15
Блок 10 [10 :0]
0 ( Глава 2. Упражнения 2. 03 из 04 )
1 3. )
2 : OVKR ( nl n2 - nl n2 nl ) SfAP DUP ROT ROT ;
3 ( 4. )
4 : 2DUP ( nl n2 - nl a2 nl o2) OVER OVER ;
5 ( 5. )
6 : NEfDUP83 ( n - n n ) 0 PICK ;
( F0RTH-83
7 : NEfDUP79 n - n n 1 PICK ;
F0RTH-79
8 : NEfOVER83 ( nl n2 - nl n2 nl ) 1 PICK ; ( F-83
9 : NEfOVER79 nl n2 - nl n2 nl 2 PICK ; F-79
10 ( 6. )
11 : NEfR0T83 ( nl n2 n3 - n2 n3 nl) 2 ROLL ; ( F-83
12 : NEfR0T79 nl n2 n3 - n2 n3 nl 3 ROLL ; ( F-83
13
14
15
Блок 14 [11 :0]
0 ( Гпава 2. Упражнения 2. 03 из 04 )
1 7. )
2 : 2SfAP83 ( 1 2 3 4 ~ 3 4 2 1 ) 3 ROLL 3 ROLL ;
3 : 2SfAP79 1 2 3 4 - 3 4 2 1 4 ROLL 4 ROLL ;
4 ( 8. )
5 : R0TSTACK83 DEPTH 1 - ROLL ;
( F0RTH-8
6 : R0TSTACK79 DEPTH ROLL ;
F0RTH-7
7 ( 9. )
8 : SPHERE ( радиус - lflO*v) DUP DDP * • 314
9 ( 10. ) • 4 » 3 / ;
10 : .B0TT0H*83 DEPTH 1 - PICK DEPTH 1 - PICK • . ; [83
11 : .B0TT0M'79 DEPTH PICK DEPTH PICK • . ; [79
12 ( 11. )
13 : NEW0D ( nl - n2 ) OVER OVER / ' - ;
14
10 М. Келли
289
Блок 12 [12 :0]
Блок 15 [15 :0]
Глава 3. Упражнения 1. 01 из 02 )
1. )
Двоичная часть результата имеет вид:
1 дает 1 3 дает 11
7 дает 111 15 дает 1111
31 д»ет 11111 63 дает 111111
127 дает 1111111 255 дает 11111111
Чксло, которое на единицу меньше чем два в степени n,
состоит из последовательности о бит, равных единице. )
2. )
Шестнадцатеричная часть результата имеет вид:
2 дает 2 4 дает 4
8 дает 8 16 дает 10
32 дает 20 64 дает 40
128 дает 80 256 дает 100 )
Шестнадцатеричное число отображает содержимое 1 байта. )
Блок 13 [13 :0]
Глава 3. Упражнения 1. 02 из 02 )
3. ) ( Ответ неизбежно будет сведен к 2., так как
11111111 равно FF в шестнадцатеричном представлении,
а 1111111111111111 равно FFFF)
( 4. ) ( Вам следует ввести FFFF 1 + . , и, если вы это
сделали, то получите результат =0. Легче запоминать и
задавать числа в шестнадцатеричном представлении, ре¬
зультат, требупций для представления более двух байт,
дает ошмбку переполнения и засыпает в стек два 0
байта.)
( 5. ) ( Прежде чем использовать описание ) DECIMAL
: HEX 16 BASE ! ; : 0CTAL 8 BASE ! ; : BINARY 2 BASE ! ;
( 6. ) ( Такие слова как AA, ВАС и т.д. помешают ис¬
пользовать соответствущме шестнадцатеричные числа,
если в их начале не стоит 0. GG не шестнадцатеричное
число)
Блок 14 [14 :0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Глава 3. Упражнения 2.* 02 из 02
5. )
- n2 ) 65535 X0R ;
- n2 65535 - ;
: N0T1
: N0T2
( 6. )
: SET? ( nl
( 7. )
: N0TSET? (
маска - f ) DUP R0T AND =
nl маска - f) DUP R0T AND
( или
Блок
16 [16 :
0]
0 i
Глава 3. Упражнения 3. 01 из 02
)
1 (
1.
2 :
.DEC.BIN ( n -
•) DECIMAL DUP . DUP U.
3
2. )
.BIN U.BIN DR0P ;
♦ (
Таблица должна
иметь спедущий вид )
5 (
1
1
1
1
6
2
2
10
10
7
3
3
11
11
8
32766
32766
111111111111110
111111111111110
9
32767
32767
111111111111111
111111111111111
10 -
-32768
32768
-1000000000000000
1000000000000000
11 ■
■32767
32769
-111111111111111
1000000000000001
12
-3
65533
-11
1111111111111101
13
-2
65534
-10
1111111111111110
14
-1
65535
-1
1111111111111111
15
Блок
17 [17 :
:0]
Глава 3. Упражнения 2.
1 )
01 из
02 )
i. 11110111 AND
б.
100 0R
в. 0 AND
г.
11111111 0R
д. 10100000 AND
e.
1111 AND
ж. 10101111 X0R
з.
11111111 X0R
и. 11111111 X0R )
2. )
ZER0-IT ( n - 0 ) 0
AND ;
3. )
NEI= ( nl n2 - f ) X0R DUP /
1 X0R ;
4. )
<> ( nl n2 - f ) X0R DUP / ;
Вше приведено быстродействупцее описание.
Другой вариант: : О ( nl n2 -- f ) = 0 =
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Глава 3. Упражнения 3. 02 из 02 )
3. )
65536 + о )
4- )
N8mCATE ( о - -n ) 65535 I0S 1 + ;
: NEVNEGATB '1 X0R 1 + ; явшется экваваленных )
65535 X0R видает то, что называето дополненех
по модулю 1; добавлевае 1 превращает его в дополве-
нае по модулп 2, которое аемет эна1. )
5. )
NEf- ( nl n2 - n3 ) NEGATE + ;
Так в действительности выполняется вычитание в ЭВМ;
то есть добавляется дополнение по модулю два. )
290
Enoi 18 [18 :0]
Бпок 21 (21 :0]
Глава 3. Упражнения 4. 01 иэ 02 )
1. ) : .lSB ( о -) 255 AND . ;
( 255 в двоичном виде равно 11111111 )
( 2.)
: MSB ( n -- ) 256 / . ;
( Это яе работает для отрицательны! чисел, rai iai
отрицательное делимое даст отрицательное частное.)
( 3. )
: .LSB1 ( n - ) 256 / 256 • - ;
( 4. )
: .LSB2 ( D - ) PAD 1 PAD С§ . ;
: .MSB2 ( о -- ) PAD 1 PAD 1+ С§ . ;
Описания в упражнениях 1 и 2 будут быстрее. ) '
5. )
: 256/ ( nl -- n2 ) PAD 1+ С§ ;
( В отличии от 256 / это игнорирует знак числа.)
Блок 19 [19 :0]
Глава 3. Упражнения 4. 02 иэ 02 )
6. )
256+ PAD ! PAD 1 + С§ 1 + PAD 1 + С! PAD I ;
0 ( Глава 4. Упрахяения 1. 02 из 02 )
1 ( 2. )
2 : LINE ( N -- строка ) 50 / ;
3 ( 3. )
4 : P0S ( - pos ) 50 MOD ;
5 ( 4. )
6 : P0SLINE (n длина — pos строка ) /MOD ;
7 ( 5. )
8 : NEI/ (nl n2 -- n3) /M0D SIAP IF DUP 0 < IP 1+ THEN THEN ;
9 : NEW0D ( nl n2 - n3 ) 2DUP N/ • - ;
10 ( 6. )
11 ( 2000 100 30 •/ . дает 6666
12 2000 30 ^0D 100 * STAP 100 • 30 / + . . таие дает 6666
13 2000 100 30 */MOD . . дает 6666 20
14 2000 30 /MOD 100 • STAP 100 • 30 /M0D ROT + . . дает 6666 20
15 Преииущество •/ очевидно, так как оно не дает переполнения)
Блок 22 [22 :0]
0 ( Глава 4. Упражнения 2. 01 иэ 02 )
1 ( 1. )
2 : .lARGEST-3 ( nl n2 n3 - ) MAX MAX . ;
3 ( 2. )
4 : 79.SMAlLEST-3 ( nl n2 n3 - nl n2 n3 )
5 3 PICK 3 PICK 3 PICK MIN MIN ; ( F0RTH-79 )
6 : 83.SMALLEST-3 ( nl n2 n3 - nl n2 n3 )
7 2 PICK 2 PICK 2 PICK MIN MIN ; ( F0RTH-83 )
8 ( 3. )
9 : TREE>5? ( nl n2 n3 - f ) 5 > МАХ МАХ 5 >
4. )
10
11 : ALL>5? (nl n2 n3
12 ( 5. )
f ) 5 > MIN MIN 5 > ;
- f ) R0T R0T MAX > ;
13 : >L0TER2? ( nl n2 n3
14 ( 6. )
15 : TEMPDlFF ( nl n2 - n3 ) - ABS ;
Bnoi 20 [20 :0]
Глава 4. Упражнения 1. 01 иэ 02 )
1. ) ( "Нориал." С округленней
по нихн. границе)
Блок 23 [23 :0]
0
2
ост
част
ост
част )
3
а.
0
5
0
5
4
6.
0
0
0
0
5
в.
2
3
2
3
б
г.
-2
-3
1
-4
7
д.
2
-3
-1
-4
8
e.
-2
3
-2
3
9
x.
0
-5
0
-5
10
3.
0
-5
0
-5
11
и.
0
5
0
5
12
к.
Деление на
0 является ошибкой )
13
02 иэ 02
Глава 4. Упражнения 2.
V- )
LARGER-MAC ( nl n2 - n3 ) ABS SIAP ABS MAX ;
«. )
NEAREST-0 ( nl n2 - n3 ) ABS SIAP ABS MIN ;
9. )
-ABS ( о о или -n n ) ABS NEGATE ;
10. )
OTHER-QUAD ( i у ~ -i -у и т.д.) NEGATE SfAP NEGATE
SIAP ;
NEfNEGATE -1
14
15
ю*
291
Блок 24 [24 :0]
Блок 27 [27 :0]
Глава 4. Упражнения 3. 01 жз 02 )
1. )
: PYRVOL ( область h ~ ▼ ) 1 2 ^OD + ;
( 2. )
: F->C ( f ~ с ) 32 - 5 9 *^OD SfAP 10 9 •/ 5 + 10 / *
( SfAP 10 9 #/ 5 + 10 / по существу округляет в большую
сторону, добавляя к чнслу .5 . )
: 10F->10C (10f - J0c ) 320 - 9 5 *^0D
SIAP 100 90 ♦/ 50 ♦ 100 / ♦ ;
( Вводнтся Эначение температуры, умноженное на 10, в ре*
зультате получается та хе, но округленная велнчнна. Это
способ работать с десятыми долями целого чнсла. )
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Глава 4. Упражнения 4. 02 нз 03 )
6. )
ТОШ ( m - Ь m ) 1000 ^0D SVAP ;
7. )
ТОРТ ( mi ft ~ ft ) ROT 5280 • ♦ ;
8. )
TOMILES ( ft - mi ft ) 5280 ^fOD SVAP ;
9. )
FTTOM ( ft - m ) 305 1000 •/ ;
10. )
T0U8TRlC ( mi fl - km cm ) TOFT FTTOM TODi
11. )
C->F ( t -- f ) 9 5 •/ 320 ♦ ;
Bnoi 25 [25 :0] Блок 21 [2t :0]
Глава 4. Упраменн 3. 02 >з 02 ) 0
3. ) 1
i. Факторнзуем [i + b]/c ) 2
(a.+.b)./.c ( i Ь с - n ) R0T ROT I SIAP / ; 3
б. Факторнзуем [ш I b/c]/c ) 4
(a.+.b./.c.)./.c ( а Ь с - n) SfAP 0VER / ROT ♦ SfAP / ; 5
в. Факторнзуем [а + Ь]А2 ) 6
(А.+.В)А2 ( а b - о J ♦ DUP • ; 7
г. Факторнзуем 3[a + b]A2 8
3.(А.+.В)Л2 ( а b - n ) (А.+.В)Л2 3 • ; 9
д. Факторнзуем [а I Ь]л4 ) 10
(A.+.B)M ( а Ь - о ) (А.+.В)А2 DUP • ; 11
Оказывается дахе сложные вырахення могут стать 12
простымн за счет фасторнзацнн) 13
14
15
Глава 4. Упрахнення 4. 03 нз 03 )
12. )
: CIRCUM ( — длнна окрухностн в нм )
10000 355 113 ♦/ 3 8 ♦/ ;
( Проблема заключается в подавленнн переполнення)
( 13. )
: IN->FT ( in - ft ) 12 ^0D SfAP 6 / ♦ ;
( 6 / + эквивалентно добавлению 2/12 остатка. )
Блок 26
[26 :0]
Блок
29 [29 :0]
i Глава
4. Упрахнення 4.
01 нз 03 )
0 (
Глава 4. Упрахнення 5. 01 нз
i.)
1
1.
1 ft-
25E15
б. 25E-5
2
4.294.967.296 равно 2 в 32-ой
в.
25E5
г. 25E-6
3 (
2. )
Д.
lE-5
e. 1
4 (
2DR0P
|d-- ]
x.
25E2
з. 500.005
5
2DUP
d - i i ]
и.
499.998
к. 499.995 )
6
2SfAP
dl d2 - d2 dl ]
( 2. )
7
20VER
dl d2 - dl d2 dl ]
: EXP* i
( ml el m2 e2- m3 еЗ ) R0T I R0T R0T 1 SfAP ;
8
2R0T
dl d2 d3 - d2 d3 dl
3. ) ( Здесь не принято никаких мер протнв переполнення) 9
BXP/ ( m e m e - m e) R0T SfAP - 2 - R0T 100 • / SfAP ; 10
Мантисса - 2 и показатель 1 100 способствуют 11
4. ) ( предотвращению переполнення ) 12
:ЛОМ ( kmm- m ) SfAP 1000 1 + ; 13
( 5. ) 14
: TOCMMM ( nm -mnm ) 10 /M0D SfAP ; 15
Не существует стандартных слов-эквивалентов
PICK, ROLL нлн DEPTH для чнсел двойной длины)
<j.)
: NKV2DR0P ( d - ) DROP DR0P ;
( 4. ) ( Это не то хе самое. Чнсло двойной
длнны содергит старшее н младпее числа,
кахдое нз которых долхно быть задублировано.)
292
Блок 30 [30 :0]
Блок 33 [33 :0]
Глава 4.
5. )
Упрахнения 5. 02 из 03
0 ( Глава 4. Упрахнения 6.'
1 ( 4. )
02 из 02
NEI2DUP
( d - d d
) OVER OVER ;
2
->DOLLARSl
I d -- n) 100 M/ ;
( MMSFORTH )
6. )
3
->DOlLARS2
( d - n) 100 0 D/ DROP ;
( Стандарт )
2SIAP83
( dl d2 -
d2 dl ) 3 ROLL 3 ROLL ; ( F-79)
4
->CENTS1 (
d - n) 2DUP ->D0LLARS1 10«
I • M- DROP ;
2SIAP79
( dl d2 -
d2 dl ) 4 ROLL 4 ROLL ; ( F-83)
5
( MMSFORTH )
?• )
6 :
-> CENTS2 (
d - n ) 2 DUP ->D0LLARS2
100 • 0 D-
2R0T83 (
dl d2 d3 •
- d2 d3 dl) 5 ROLL 5 ROLL ; ( F-83]
I 7
DROP ;
( Стандарт )
2R0T79 (
dl d2 d3
- d2 d3 dl) 6 ROLL 6 ROLL ; ( F-79]
I * I
I 5. )
8. )
9 :
FRAC1 2SIAP ROT 0 D/ ROT 0 D* ;
( Стандарт )
20VER83
( dl d2 -
dl d2 dl ) 3 PICK 3 PICK ; ( F-83)
10 :
FRAC2 М‘/ ;
( MMSFORTH )
20VER79
9. )
( dl d2 -
dl d2 dl ) 4 PICK 4 PICK ; ( F-79)
11
12
2R0LL83
2R0LL79
2 • DUP
2 • DUP lf
ROLL SVAP
ROLL SVAP
1-
ROLL
ROLL
F0RTH-83) 13
F0RTH-79) 14
15
Блок 31 [31 :0]
03 из 03
F0RTH-83
F0RTH-79
M/MOD ( d о -
3. )
Как M* так и M/MOD воспринимают числа со знаком,
то время как U* и U/MOD работают с числами без
знака.)
Блок 34 [34 :0]
Глава 4. Упрахнения 5.
10. )
2PICK83 2 • DUP PICK SIAP 1- PICK
2PICK79 2 • DUP 1+ PICK SIAP PICK
11. )
S->D ( о -- d ) HIJ ;
( 12. )
( H1/ будет содержап 0. )
( 13. )
10AN 1 SIAP 0 DO 10 • LOOP ;
ВЕР. #PT 1- 10*N 0 D/ ;
AFT. 2DUP ВЕР. #PT 1- 10*N 0 D* D- ;
Таким образом можно разделить целую и дробную частн
чнсла двойной длины. )
Блок 32 [32 :0]
( Глава 4. Упрахнения 6. 01 нз 02 )
( 1. )
( I UM* выполнит зто. Исполъзуютса числа без 3Haia.
( 2. )
M* ( о D -- d ) 0 R0T 0 D* ;
U+ d D - d ) 0 Dl ;
M- ( d D -- d ) 0 D- ;
M/ ( d n -- n ) 0 D/ DR0P ;
о о ) 0 D/M0D DR0P SIAP DROP ;
10
11
12
13
14
15
Глава 4. Упражнения 7.
1. )
а. X 5.5 X 1200 F+ F.
в. DEGREES X 55 SlN 2DUP
X 1- SQR F.
01 из 01 )
6. X 23 X 5 F/ LOG F.
F* X 45 COS 2DUP F* F* F.
д. X 3.25 2DUP F* PI P* F.
г.
2. )
RECT ( nl n2 - ) I-F FDF 5 ROLL l-F FDF DF* DF.
3. )
NEIFABS
<• )
HYPOT (fl f2
5. ) : TABLE1
1 градусу)
fl - f2 ) 2DUP F* SQR
F* 2SIAP 2DUP F* Ft SQR
.R ( приращение равно
f3 ) 2DUP
46 DO I 3
I-F SIN 10 F.R I l-F TAN 10 F.R CR
TABlE2 0 451 DO I I-F X 10 F/ 2DUP 6 F.R
2DUP SIN 10 F.R TAN 10 F.R CR LOOP
приращение равно одной десятой )
Блок 35 [35 :0]
) 2 (
01 из 03
Глава 4. Упражнения 8.
1- )
а. 5 8 + X 5.5 X 6.5 Ft б. X 35 10 >87 Ft SIN
в. X -55 FNECATE )
2. ) ( г. 5 . X 35 2DUP COS 2SIAP SIN F+ F. )
87FABS ( fl - f2 ) FDUP Р‘ SQR ;
6 : 87CINT ( fl - n ) 87> •;
7 : 87I-F ( D - f ) >87 ;
8 : 87RAD PI X 180 F/ ;
9 : 87L10 X 10 LOG ;
10 ( 3. )
11 : FACT 1001 2 X 1 DO I >87 F* LOOP F. ;
12 ( Выдает результат .4023872600770938E2568; 100 итераций
13 требует 10.1 се1унд. Выполнение цикла требует 7.1
14 секунд. T.o. 100.000 преобразований в форму с ппаванцей
15 точюй и уиножений требуют 3.1 секунды. )
293
Bnoi 36 [36 :0]
Блок 39 [39 :0]
Глава 4. Упрахнения В. 02 иэ 03 )
<• )
< С клавиатуры
% 113 X 355 F/ PI P- F.
дает 2.66764E-7 или ошибку около 0.00000003 )
( 5. )
: ANGlE ( i у - угол ) DEGREES PHASE ;
( «• )
: HYP0T ( i у - угол ) DEGREES MAG ;
( 7. ) : C0NC-CHANGE ( pH B-ch - X)
FSIAP FNEGATE 10л P/ % 100 F* ;
: DELTAPH ( pH H-ch — pHl) ( Вичис. новое H, затеи
новое pH) FSIAP FNEGATE 10A P+ LOGlO FNEGATE ;
( Практически это нельзя сделать в ранках целочисленной
арифметики, не написав програииу разнерон в несколько
блоков с таблицаии логарифмов и степеней 10.)
Блок 37 [37 :0]
( Глава 4. Упражнения В. 03 из 03 )
( ». )
: ARM-MOYE ( il у1 i2 y2 -
— приращение угла приращение радиуса )
DEGREES P0VER P0VER MAG DP87> PHASE DP87>
P0VER P0VER PHASE PR0T PR0T MAG DP>87 DP>87
PR0T F- PR0T PR0T PSfAP P- PSfAP ;
( Поскольку не существует FPICK или 2PSVAP числа
запоминаются в **обычном" стеке с помощью DP87> и
восстанавливаются DP>87. )
Блок 38
[38 :0]
( Глава
1 \
5. Упрахнения 1. 01 из 02 )
\ !• /
: BS 8
EMIT ;
( 2. )
: PAGE
12 EMIT ;
( 3. )
: CRS 0
DO CR LOOP ;
( 4. )
: DASHES 0 DO 45 EMIT LOOP ;
( Это эквивалентно : DASHES 0 DO ." -" LOOP
0
( Глава 5. Упрахнения 1.
02 из
02 )
1
( 5. )
2
: MMENU ." MAIN MENU** ;
THIS
.** This is the
" ;
3
: 1ST .** first " ;
2ND
.** second " ;
4
: 3RD .’* 3rd ’* ;
4TH
.’* PORTH - ;
5
: СНЕ ." choice" ;
6
: MENU 9 SRACES 5 DASHES SPACE MMENU SPACE 5 DASHES 2
7
CRS 40 DASHES CR 4 SRACES .
.’* А ”
6 DASHES SPACE
THIS 1ST
8
СНЕ CR 4 SRACES ,
." В ”
5 DASHES SPACE
THIS 2ND
9
СНЕ CR 4 SRACES ,
.'* с -
8 DASHES SPACE
THIS 3RD
10
СНЕ CR 4 SRACES
.** D м
6 DASHES SPACE
THIS 4TH
11
СНЕ CR 40 DASHES CR 5 DASHES SPACE
12
.’* fHAT IS YOUR CHOICE, PLEASE?*’
SPACE 5 DASHES
CR ;
13
(«•)
14
: $. COUNT TYPE ;
15
Блок 40 [40 :0]
0 ( Глава 5. Упрахнения 2. 01 из 03 )
1 ( 1. )
2 : PL0T1 CR DEPTH 0 D0 MlN XS CR L00P ;
3 ( Заметьте, насколько элегантнее MIN, чем
4 IP ... THEN, как это сделано в главе 1. )
5 ( 2. )
6 : PL0T2 PRINT CR DEPTH 0 D0 79 MIN XS CR L00P CRT ;
7 ( 3. )
8 : YS 0 D0 89 EMIT L00P ;
9 : PL0T3 СЯ DEPTH 2/ 0 DO YS CR XS CR LOOP ;
10 ( 4. )
11 : PL0T4 CR DEPTH 0 DO I . 3 SPACES XS CR LOOP ;
12 ( 5. )
13 : PL0T5 CR DEPTH 0 DO I . 3 SPACES 1- SPACES .’* Xм
14 CR LOOP ; ( 1- необходима перед SPACES для того,
15 чтобы поместить '*Xм в конец строки. )
Блок 41 [41 :0]
0 ( Глава 5. Упрахнения 2. 02 из 03 )
1 < в.)
2 : PL0T6 CR DEPTH 0 DO I . SPACE 1- 75 1000 •/
3 SPACES ." X" CR LOOP ; ( Заметьте, что
4 масштабирование выполнено с использованием 75. )
5 ( 7. )
6 : PL0T7 CR DEPTH 2/ 0 DO SIAP XS YS CR LOOP ;
7 ( 8. )
8 : PL0T8 CR DEPTH 2/ 0 DO SIAP 1- SPACES ." X" 1-
9 SPACES ." Y" CR LOOP ;
10 ( 9. )
11 : PL0T9 CR DEPTH 2/ 0 DO SIAP 1- SPACES ." X" DUP
12 1- SPACES .” Г . CR LOOP ;
13 ( 10. )
14 : PLOTlO CR DEPTH 2/ 0 DO SIAP 1- SPACES .'' X" DUP
15 60 1000 •/ 1- SPACES ." Г . CR LOOP ;
294
Блок 42 [42 :0]
( Глава 5. Упражнения 2. 03 из 03 )
1 И. )
: PL0T11 CR DEPTH 2/ 0 SfAP 0 DO OVER SfAP - 3 PICK
SIAP 0 DO CR ." ." LOOP SPACES ." X " SfAP OVER
. . LOOP DROP ;
( Это описание можно упростить, использовав переменную
или второй стек, стек возвратов, который будет описан
в следуюцих главах. Bu можете убедитыятеперь, на¬
сколько проще временное запоминание чисел в
PAD как в: )
: PL0T12 CR DEPTH 2/ 0 PAD ! 0 DO OVER OVER PAD § -
0 DO CR ." ." LOOP SPACES ." X " DUP PAD ! . . LOOP ;
( Хотя и не намного короче, PL0T12 имеет более простые
манипуляции со стеком, так как предшествующая
величина X [или 0 в начале] записана в PAD. )
Блок 45 [45 :0]
0 ( Глава 5. Упражнения 3. 03 из 03 )
1 ( 8. )
2 : .PH0NE ( nl n2 n3 -) 0 <f | | | | 45 HOLD DROP
3 DROP 0 f | | 32 HOLD DROP
4 DROP 41 HOLD 0 | | | 40 H0LD |> TYPE ;
5 ( Заметьте, что можно преобразовывать несколько чисел
6 одновременно, но DR0P DR0P необходимо для удаления
7 из стека частного двойной длины, исключая nl,
8 которое удаляется f> .)
9 ( 9. )
10 : PL. ( d - ) <| #PT 1 >
11 IP #PT 1- 0 D0 f L00P THEN 46 H0LD fS |> TYPE ;
12 ( Это иллюстрирует, какая программа может находиться
13 между </ и f> . При вводе .1234 будет отображен
14 начальный 0.)
15
Блок 43 [43 :0]
( Глава 5. Упражнения 3. 01 из 03 )
( 1. )
: UD$. ( ud - ) <f f | 46 HOLD fS 36 H0LD f> TYPE ;
( 2. )
: UD$. ( u - ) 0 <| | | 46 HOLD fS 36 H0LD |> TYPE ;
( 3. )
: S|. ( n - ) DUP 0< NEGATE SfAP 0VER DABS
<f | | 46 H0LD fS 36 H0LD R0T SIGN |> TYPE ;
( Здесь и в следущем упражнении NEGATE, как обсужда¬
лось ранее, в F0RTH-83 присутствовать не должен.)
( 4. ) : S$.R ( nl n2 ~) SfAP DUP 0< NEGATE SfAP
0VER DABS </ | f 46 H0LD fS 36 H0LD ROT SIGN |
R0T 0VER - SPACES TYPE ;
( Длина поля, n2, хранится в стеке пока это необхо¬
димо, а затем используется R0T 0VER - SPACES, как
в описании U.R в тексте. )
Блок 44 [44 :0]
( Глава 5. Упражнения 3. 02 из 03 )
( 5. )
: .L ( nl n2 - ) SfAP DUP 0< NEGATE <f fS f>
R0T 0VER - R0T R0T TYPE SPACES ;
( Запомните, никакого NEGATE в P0RTH-83 нет. )
( б. )
: .DATE ( d - ) <| f | 47 H0LD | f 47 H0LD
f f f> TYPE ;
( 7. )
: .MDY ( d - )
| f | 32 H0LD 46 H0LD 121 H0LD 32 H0LD
f | 32 H0LD 46 H0LD 100 HOLD 32 H0LD
t | 32 H0LD 46 HOLD 109 HOLD f> TYPE ;
( 121, 100, и 109 являются ASClI "у", "d" и "m". )
Блок 46 [46 :0]
0 ( Глава 6. Упражнения 1. 01 из 04 )
1 ( 1. )
2 VARIABLE РЕЕТ VARIABLE INCHES
3 : P->I ( ~ ) РЕЕТ § 12 • INCHES ! ;
4 ( 2. )
5 2VARlABLE DPEET 2VARIABLE DINCHES
6 : DP->I DPEET 2§ 12 0 D* DINCHES 2! ;
7 ( 0 формирует 12 чисел двойной длины. )
8 ( 3. ) : NEf2! ( d адр - ) SfAP 0VER 2SfAP 2* ! ! ;
9 ( 2! может использоваться для запоминания двух чи-
10
сел одинарной длины, nl n2 адр 2! запоминает nl
11
по адресу "адр", а n2 по адресу "адр+2" .)
12
4.
13
: NEfl! ( n адр - ) DUP § ROT ♦ SfAP ! ;
14
( 5. )
15
: NEf§ ( адр - n ) DUP U С§ 256 • SfAP С§ ♦ ;
Блок 47 [47 :0]
0
( Глава 6. Упражнения 1.
02 из 04 )
1
( 6. ) : NEf! ( n адр -|
I
2
OVER 0VER SfAP 256 / SfAP lf С! С
3
( 7. )
4
: VARSfAP ( адр 1 адр2 ~]
| DUP § PAD ! SfAP
5
DUP 0 ROT ! PAD § SfAP
6
( 8. )
7
CREATE lfEEK 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
8
CREATE 2fEEK 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
9
( Оба слова засылают при
использовании в стек )
10
( 9. ) ( свои адреса.)
11
: !SUN ! ; : !MON
2f 1 ; : !TUE 4 !
12
: !fED 6 i ! ; : !THU
8 ♦ ! ; : !FRI 10 ♦
13
: !SAT 12 f ! ;
14
( Все слова имеют диаграмму преобразоания стека
15
( n адр — )
295
6noi 48 [48 :0]
Bnoi S1 [S1 :0]
Глава б. Упрахнения 1. 03 иэ 04 )
10. )
OSUM 0 ; : WON 2+ в ; : 9TUE 4 + 0 ;
6VED « + в ; : 0THU 8 + 0 ; : 0FRI 10 + в ;
0SAT 12 + § ; ( Кахдое слово имеет cieyy
преобразования cteia (адр — n)
< H. )
: RSIAP ( nl n2 адр - ) SIAP 2 • 0VBR +
ROT 2 • ROT ♦ VARSIAP ;
( Кахдое число долхно умиохашя иа 2 и добавляться
i адресу тах, чтобы н содержимое иохно было бы
обменяп с ооиощио VARSIAP )
( 12. )
CREATE CNT 0 С, 0 С, 0 С, 0 С, 0 С, 0 С, 0 С,
Блок 49 [49 :0]
Глава 6. Упрахнення 1.
13. )
+SUM 0 * +! 1 0 CNT f
+M0N 2 * l! 1 1 CNT ♦
+TUE 4 ♦ ♦ ! 1 2 CNT ♦
fIED 6 ♦ +! 1 3 CNT +
lTHU 8 f f! 1 4 CNT +
♦FRI 10 f + ! 1 5 CNT f
fSAT 12 ♦ + ! 1 6 CNT +
04 из 04
Кахдое слово имеет схему преобразования стека
D адр --)
14. )
DAY-AVE ( день -
DUP DUP
- средн. ,
2 • lfEEK
2 • 2VEEK
SIAP
SIAP CNT ♦
С§ /
Блок 50 [50 :0]
Глава 6. Упрахнения 2.
1. )
NEIFILL ( адр n с ~ )
2. ) 1+ ROT 1- CMOVE
NEIERASE ( адр n - )
3. )
INITIALIZE ( адр n -
4. )
ARR-C0PY1 ( адр1 адр2
ARR-C0PY2 ( адр1 адр2
01 из 01
ROT SIAP OVER С! DUP
0 FILL
n —
2 • CMOVE
MOVE ;
5. )
PAD
PAD
PAD
ARR-EXCH ( адр 1 адр2 n ~)
MOVE
MOVE
Запишем счетчик в PAD) OVER PAD 2
Переносим адр1 в PAD+2 ) DUP ROT
Переносим адр2 в адр1 ) PAD 2 +
SIAP PAD § MOVE ( Переносим PAD+2 в адр2
Глава 6. Упрахнения 3.
1. )
CVARIABLE CREATE 1 ALLOT
4VARIABLE CREATE 4 ALLOT
2. )
ARRAY CREATE 2 * ALLOT ;
01 из 02
6 ( 3. )
7 : CARRAY CREATE ALLOT ;
8 : 2ARRAY CREATE 4 1 ALLOT ;
9 : 4ARRAY CREATE 8 * ALLOT ;
10 ( 4. )
11 VARIABLE lLENGTH VARIABLE 2LENGTH
12 : C0NVERTl-2 lLENGTH § l->2 • 2LENGTH !
13 : C0NVERT2-1 2LENGTH § l->2 / lLEHGTH !
14 фициент мохно путем замени 12 [см. випе
15 или путем n ' l->2 ! [79], или n ' {или
Блок 52 [52 :0]
12 CONSTANT l->2
»
; ( Сменить коэф-
и рекомпиляции
[’]] >BODY ! [83])
0
1
2 :
3 (
4
5
6
7
10
11
12
13
-14
lS
Глава 6. Упрахнения 3. 02 из 02 )
5. )
X->Y ( i ~ у ) А § • В 9 ♦ ;
Замена операции путем изменения содерхимого пе¬
ременных А и В соответствует n А ! или n В ! .)
в.)
В Форт-79 здесь нет различия. Оба засылают в стек
адрес, где запомнено значение. ' >BODY является
эквивалентом для Форт-83. )
7. )
SET-FEET ( - ) 12 [Ч T0-1NCHES >BODY ! ;
SET-YARDS ( - ) 36 [’] ТО-INCHES >BODY ! ;
в описании-двоеточии Долхно использоваться [']
вместо ’.)
Блок 53 [53 :0]
0
1
2
3
4
5
6
7
8
3
10
11
12
13
14
15
Глава 6. Упрахнения 4. 01 из 06 )
1. )
SIR ." Dear Sir:” ; : MADAM ." Dear Madam:” ;
SORM .” Dear Sir or Madam:” ; VARIABLE HELLO
SALUTATION HELLO § EXECUTE ;
Смена приветствия путем FIND <name> HELLO ! в
Форт-79 или ' <name> HELLO ! в Форт-83. )
2. )
CREATE CHOICES FIND PRINT , FIND CRT , FIND PCRT ,
В Форт-83 используйте ’ вместо FIND. )
CHOOSE . PRINTER (1), SCREEN(2), OR B0TH(3) M KEY
49 - 0 MAX 2 MIN 2 * CHOICES I § EXECUTE ;
Нажатие клавиши преобразуется в число от 0 до 2
путем вычитания значения ASCII 1 [49]. 0 МАХ 2 MIN
гарантирует, что неверное нахатие клавиши не вы¬
ведет из строя систему. )
296
Блок 54 [54 :0]
Блок 57 [57 :0]
Глава 6. Упражнения 4. 02 из 06 )
3. )
VARIABLE C0UNT1 VARIABLE C0UNT2 VARIABLE COUNT3
Глава 6. Упрахнения 4.
7a. )
05 из 06
! 0 C0UNT2
VARIABLE fT2
0 fT2 !
0 C0UNT1
VARIABLE fTl
0 fTl !
( 4i. )
: CLASS ( wt -- о
( 4k. )
CREATE COUNTADDRS COUNTl ,
CREATE fTADDRS fTl
( 4c. )
: Bf ( wt ~) DUP CLASS 2
COUNTADDRS ♦
! 0 COUNT3
VARIABLE fT3
0 fT3 !
100 / 0 MAX 2 MlN
COUNT2
fT2
COUNT3
fT3
' DUP 1 SfAP
§ +! fTADDRS + § +!
2
CREATE
DV0RAK
32
С,
123
С,
95
С,
37
С,
35
С,
33
С,
41
С,
3
45
С,
36 С,
94
С,
64
С,
43
С,
87
С,
56
С,
86
С,
90
С,
4
54
С,
91 С,
55
с,
53
с,
51
С,
49
С,
57
С,
48
С,
50
с,
5
52
С,
83 С,
115
с,
119
с,
61
С,
118
С,
122
С,
38
С,
65
С,
6
88
С,
74 С,
69
с,
62
с,
85
С,
73
С,
68
С,
67
С,
72
с,
7
84
С,
78 С,
77
с,
66
с,
82
с,
76
с,
34
С,
80
С,
79
с,
8
89
С,
71 С,
75
с,
60
с,
81
с,
70
с,
58
С,
47
С,
92
с,
9
93
с,
40 С,
42
с,
96
с,
97
с,
120
с,
106
С,
101
С,
46
с,
10
117
с,
105 С,
10(
) С,
99
с,
104
с,
116
с,
110
С,
109
С,
98
с,
11
114
с,
108 С,
63
с,
112
с,
111
с,
121
с,
103
С,
107
С,
44
с,
12
113
с,
102 С,
59
с,
63
с,
124
с,
125
с,
126
С,
—>
13 ( Это таблица преобразования клавиатуры QfERTY
14 Она начинается с пробела [ASCII 32], который
15 тичное значение для обоих типов клавиатуры. ]
в Dvorak.
имеет иден-
—>
Блок 55 [55 :0]
Глава 6. Упрахнения 4. 03 из 06 )
4d. )
: SUMMARY ( n -- ) CR .” In class ” DUP . ." there are м
2 1 DUP COUNTADDRS + § § . ." bolts vith a total weight
of M DUP fTADDRS ♦ § § . CR . and a mean weight of "
DUP fTADDRS ! § § SfAP COUNTADDRS ♦ § § / . .” ." CR ;
( 5. )
CREATE COUNT 0 , 0 , 0 , CREATE fEIGHTS 0 , 0 , 0 ,
: NBf ( wt - ) DUP CLASS 2 • DUP 1 SfAP COUNT ! ♦!
fEIGHTS ! f! ;
( Упрахн. 4 и 5 иллюстрируют 2 способа сделать одну и ту
xe вещь. 4 такхе показывает насколько легко использовать
переменные совместно с таблицами. Обратите внимание на
сходство описаний fT и NfT. NfT лучше, так как оно требу¬
ет меньше памяти и времени, хотя 4 имеет небольшое преиму¬
щество контроля значений с помощью, например, fTl § . .)
Блок 56 [56 :0]
Глава 6. Упрахнения 4' 04 из 06 )
5. ) : SMPHRASE .“ under 200 grams, is " ;
: MDPHRASE ." 100 to 200 grams, is " ;
: LGPHRASE ." more then 200 grams, is " ;
CREATE PHRASES FIND SMPHRASE , FIND MDPHRASE , FIND
LGPHRASE , ( В Форт-83 следует использовать ’
вместо FIND . )
( б. )
0 CONSTANT SMALL 2 CONSTANT MEDIUM 4 CONSTANT LARGE
SAY ( о - о ) DUP PHRASES ♦ § EXECUTE ;
CNT ( n - ) ." Total count, " SAY COUNT f § . ;
fEIGRT ( n - ) ." Total weight, " SAY fEIGHTS ♦ § . ;
AVERAGE ( n - ) . AVERAGE weight, " SAY
DUP fEIGHTS ♦ § SfAP COUNTS ♦ § / . ;
SAY удаляет ненухные части следующих слов; эта техника,
называемая разбором, широко используется в Форте.)
Блок 58 [58 :0]
Глава 6. Упрахнения 4. 06 из 06 )
7b. ) : DKEY KEY DUP 31 > 0VER 127 < AND
IF 32 - DV0RAK ♦ С§ THEN ;
DKEY используется такхе, где обычно )
8. ) ( применяется KEY )
CREATE KEYBD FIND DKEY , FIND KEY , VARIABLE ?KBD
( В Форт-83 используйте ' вместо FIND. C0NSTANT была
бы быстрее, но скорость здесь не вахна.)
: CHOICE CR .” Type 1 for Dvorak” CR ." Type 2 for
QfERTY" KEY DUP 49 - 0 МАХ 1 MIN 2* KEYBD
♦ § ?KBD ! ;
: NEfKEY ?KBD § EXECUTE ;
: TEST BEGIN NEfKEY EMIT 0 UNTIL ;
( Используется для теста клавиатуры. )
Блок 59 [59 :0]
Глава 6. Упрахнения 5.
i.)
9 ARRAY NUMBERS
CLASS ( вес —
COUNTS ( D --
2. )
9 ARRAY fTSUM
!f ( вес счет
3. )
.TOT-#S CR 10
.T0T-fT CR 10 0 D0
.AVE-fT CR 10 0 D0
4. )
01 из 03
10 / 0 МАХ 9 MIN ;
) CLASS 1 SfAP NUMBERS t!
DUP CLASS fTSUM f!
D0 I NUBMERS § 6 .R L00P ;
I fTSUM § 6 .R L00P ;
I fTSUM §
I NUMBERS § / 6 .R L00P ;
!fT ( w --) DUP COUNTS !f .T0T-fT .T0T-#S .AVE-fT
INIT 10 0 D0 D0 0 I fTSUM ! 0 I NUMBERS ! L00P ;
Используйте INIT для начального обнуления массива)
297
Блок 60 [60 :0]
Блок 63 [63 :0]
Глава 6. Упрахнения 5. 02 из 03
9 ARRAY NUMfTS
CLASS ( вес -
COUNTS ( вес
!f ( wt ~ )
.TOT-#S CR 10
.TOT-fT CR 10
.AVB-fT CR 10
!fT ( w -
0
1 9 ARRAY NUMtTS 1
10 / 0 МАХ 9 MlN ; ( Аналог. CLASS ранее) 2
) CLASS 1 0 ROT NUMITS ♦ ! ; 3
DUP CLASS 1 SIAP NUMITS +! ; 4
0 DO 0 I NUMITS § 6 .R LOOP ; 5
0 DO 1 1 NUMITS § 6 .R LOOP ; 6
0 DO 1 I NUMITS § 0 I NUMITS § / 6 .R LOOP ; 7
DUP COUNTS !I .TOT-IT .TOT-fS .AVE-IT ; 8
( Это идентично предшествуадему примеру. ) 9
: lNlT 10 0 DO 0 0 I NUMITS ! 0 1 I NUMITS ! LOOP ; 10
( Заметьте, насколько это похохе на предшествущее упрахн. 11
Использование матрицы экономит немного место за счет потери 12
некоторого времени. Реальное преимущество матрицы сказыва- 13
ется при размерности больше двух или, когда для математи- 14
ческих расчетов необходима матричная алгебра. ) 15
Блок 61 [61 :0]
Глава 7. Упрахнения 7. 02 из 03
2. )
а. = ROT ROT = AND
в. <> R0T ROT = AND
д. <>'R0T R0T <> AND
x. < R0T R0T > AND
з. OVER = SIAP ROT = AND
и. 0VBR < SIAP R0T < AND
3. )
?REM=0 M0D 0=.;
4. )
?REM M0D 0= N0T ;
5. )
?0PP0SITE ♦ 0= ;
NEI= - 0= ;
6. = ROT R0T = 0R
r. <> R0T ROT = OR
e. = R0T R0T = X0R
Блок 64 [64 :0]
Глава 6. Упрахнения 5. 03 из 03 ) 0
6. ) 1
CREATE NEISTACK 32 ALLOT VARIABLE STACKP0S 2
NEISTACK STACKPOS ! 3
XPUSH ( о -- ) STACKPOS § ! 2 STACKPOS ♦! ; 4
XPOP ( - о ) -2 STACKPOS +! STACKPOS § § ; 5
PUSH ( о ~) STACKPOS § NEISTACK 28 ♦ MIN STACKPOS ! XPUSH ; 6
POP ( - n) STACKPOS § NEISTACK 2 ♦ MAX STACKPOS ! XPOP ; 7
( Когда стек станет пуст, POP выдаст число, которое было 8
занесено туда последним. Аналогично, когда стек заполнен, 9
PUSH перезапишет верхнее число, в то время как XPUSH и XP0P 10
разрушит систему из-за переполнения стека. ) 11
( 7. ) 12
NEIDR0P P0P DR0P ; ( Форт в действительности контроли-) 13
NEIDUP P0P PUSH PUSH ; ( рует стек аналогичным образом) 14
NEISIAP P0P P0P SIAP PUSH PUSH ; 15
Глава 7. Упрахнения 1. 03 из 03
6. )
D= D- 0= SIAP 0= AND ;
7. )
C0MP DUP 0< NEGATE SIAP 0> * ;
Блок 62 [62
:0]
Блок
65 [65
:0]
( Глава 1
/ i \
К Упрахнения 1. 01 из 03 )
!I
Глава 7
1 )
. Упрахнения 2.
01 из 02 )
\ * • /
: NEI> (
nl
n2 -
f ) SIAP < ;
1 \
2 :
NEIABS
( -n — n или n -
- n ) ?DUP 0< IP 1
: NEI0<
n
- f
| 0 < ;
3 (
2. )
: NEI0>
D
- f '
) NEGATB NEI0< ;
4 :
/0? ( n
~) ?DUP IP / ELSE DROP
: NEI<=
nl
n2 -
- f ) - 1- NEI0< ;
5 (
3. )
.м Divide by
0 error" THEN ;
: NEI>=
nl
n2 -
- f ) - II NEI0> ;
6 :
TYPE<10
( адр n —) DUP
10 > IP TYPE ELSE
: NEI= (
nl
n2 -
f ) OVER OVER NEI<= ROT
7 (
4. ) :
lTASK ." А " ; :
2TASK ." T и ; :
ROT NEI>= AND ;
8 :
mm-
TASK ( - ) KEY DUP 65 =
: NEI<> |
[ nl
n2 -
- f ) - DUP NEI0< SIAP NEI0> OR ;
9
IP
lTASK
: NEI0= 1
I n
- f
) 0 NEI= ;
10
ELSE
DUP 84 =
11
IP
2TASK
12
ELSE
DUP 88 =
13
IP
3TASK THEN
14
THEN
15
THEN DROP ;
IP NEGATE THEN
298
Блок 66 [66 :0]
Блок 69 [69 :0]
Глава 7. Упражнения 2. 02 нз 02 )
5. )
: UPPER-KBY ( - с ) Ш DUP 96 > OVER 123 < AND
IF 223 AND TMEN ;
( «. )
: NO-CONT-KEY ( - с ) UPPER-KEY DUP 32 <
IF 64 + THEN ;
( 7. )
: ALFA-KEY ( - с ) NO-CONMEY DUP 47 > OVER 58
< AND IF DROP THEN ;
( 8. )
: D->S ( d -- о или d - d ) ?DUP IF THEN ;
( : D->S DUP 0= IF DROP THEN ; также работает,
но медленнее.)
Блок 67 [67 :0]
Глава 7. Упражнения 3. 01 из 03 )
1. )
: ?END ." Do you w*nt to quit? (Y/N) ” KEY 89 =
IF CR .“ Do you want to save the stack? (Y/N) “ KEY
89 = lF ." ОГ CR QUIT ELSE .“ 0K" CR ABORT THEN
THEN ;
2. Нет. )
3. )
Первое остановит программу, вторые два обеспечат
только выход из слова ?223 .)
( 4. Нет. )
( 5. )
: 0? ( nl -- n2) ?DUP 0= IF ." Nunber is iero "
( 6. ) ABORT THEN ;
: =IF-ABORT = IF ABORT THEN ;
Блок 68 [68 :0]
Глава 7. Упражнения 3. 02 из 03 )
7. )
: +RANGE-AB0RT ( nl o2 - nl n2 ) OVER 0VER 0 R0T 0
Dl 0= 0= IF ." Add overflow error ? ” ABORT THEN ;
( «. )
: MRANGE-ABORT +RANGE-ABORT OVER OVER 0 ROT 0
D' 0= 0= IF ." Multiply overflow error ? “
ABORT THEN ;
( 9. )
: STACK-T00-BIG ( - )
DEPTH 15 > IF ." Stack too big ? " ABORT THEN ;
0 ( Глава 7. Упражнения 3. 03 из 03
1 ( 10a. )
2 : DOTHAT ." That " ;
3 : D00THER .” Other " ;
4 : lTASK ( о --
5 ( 10b. )
6 : 2TASK ( о -
0= IP DOTHAT THEN ;
IP DOTHAT THEN ;
0= IF DOTHAT ELSE D00THER TNEN
IP DOTHAT D00THER THEN ;
7 ( 10c. )
8 : 3TASK ( о
9 ( 10d. )
10 : 4TASK ( D
11
12
13
14
15
ВлоЕ 70 [70 :0]
0 ( Глава 7. Упрахнения 4. 01 из 02 )
1 ( 1. )
2 CREATE MATH , PIND ♦ , PIND - , PIND • , PIND / ,
3 ( В Форт-83 используйте ' вместо PIND )
4 : ARITH ( nl n2 n3 - ) 1- 2* MATH ♦ § EXECUTE ;
5
6 : ARITH' ( nl n2 n3 ~ n4 ) DUP 0 > OVER 5 < AND
7 IP 1- 2* MATH ♦ § EXECUTE ELSE DROP DROP DROP 0 THEN
8 ( 2. )
9 : ARITH'’ ( nl n2 n3 - n4 ) NCASE 1 3 4 м ♦ - • /
10 OTHERWISE DROP DROP 0 CASEND ;
11 ( 3. )
12 : NEVARITH ( nl n2 - n3 ) KEY ACASE ♦-•/" ♦ - 1 /
13 OTHERVlSE DROP DROP 0 CASEND ;
14
15
Блок 71 [71 :0]
0
1
2 :
3
4
5
6
7 (
10
11
12
13
14
15
02 из 02
Глава 7. Упрахнения 4.
4. )
NEVARITH' IEY DUP 43 = IP DROP + ELSE
DUP 45 = lF DROP - ELSE
DUP 42 = IF DROP •
47 = IF
ELSE
/ ELSE
5. )
NEVARITH’’
в.
DROP DROP 0 THEN THEN THEN THEN
~) KEY ACASE ♦-•/" ♦ - • /
OTHERVISE ." Incorrect input
ABORT CASEND ;
Еслн вы хотите изменить заданне путем замени
содерхимого вектора. Векторное исполнение кроме
того быстрее. )
299
Бло1 72 [72 :0]
Bnoi 75 [75 :0]
( Глава I. Уорахяеная 1. 01 13 06 )
( 1. )
: ASCIim ( - ) CR H SIAP
DO I 4 .R 3 SPACES ! EHIT CR LOOP ;
< 2. )
: ASCIICBAR ( - ) CR 33 9 0 DO
10 0 DO DUP I J 10 ’ + + 4 .R lOOP CR
10 0 DO DUP I J 10 ’ + + 3 SPACES EMIT LOOP CR
LOOP DROP ;
( 3. )
: ZERDIAG ( адр - )
5 0 DO DUP I 5 ♦ I + 2 • + 0 SIAP ! LOOP DROP ;
Блок 73 [73 :0]
( Глава (. Упражненн 1. 02 аз 06 )
< 4> 1 ,
: SUMCOL ( адр — nl n2 n3 n4 o5 )
5 0 DO 0 SfAP
5 0 DO DUP I 1 ♦ 2 * ♦ § ROT + SfAP LOOP
LOOP DROP ;
( 5. )
: ХА5 tn - nA5 ) DUP 4 0 DO OYER • LOOP ;
Блок 74 [74 :0]
( Глава 8. Упражнения 1. 03 из 06 )
( 6. )
: D" ( nl n2 ~ ) ?DUP 0=
IF DROP 1.
ELSE DUP 1 =
IF DROP 0
ELSE 0. ROT 0 2SfAP 20VER 2SfAP DROP 1- 0
DO 20VER D*
LOOP 2SfAP 2DR0P
THEN
THEN ;
( 7. )
: DUMP ( адр - ) CR BASE § SfAP HEX 10 0
DO 16 0
DO DUP I J 16 • ♦ ♦ С§ 3 .R LOOP CR
LOOP DROP BASE ! ;
0 ( Глава 8. Упражнения 1 04 из 06 )
1 (•. )
2 : DUMP2 CR BASE § SfAP HEX 10 0
3 DO DUP I 16 * ♦ 5 .R 16 0
4 DO DUP I 1 16 * ♦ ♦ С§ 3 .R LOOP CR
5 LOOP DROP BASE ! ;
6 ( 9. )
7 : DUMP3 ( адр - ) CR BASE § SfAP HEX 5 0
8 DO DUP I 16 * + 5 U.R 16 0
9 DO DUP I 1 16 * + + C6 3 .R LOOP
10 CR 5 SPACES 16 0
11 DO DUP 2 SPACES I 1 16 1 + + С§ DUP 32 < OVER
12 127 > OR IF DROP 46 EMIT ELSE EMIT THEN
13 LOOP CR
14 LOOP DROP BASE ! ;
15
Блок 76 [76 :0]
0 ( Глава 8. Упражнения 1. 05 из 06 )
1 ( 10. )
2 VARIABLE SPCS
3
4 : COUNT-SPCS ( адр ~) 1024 0 DO DUP I ♦ С§ 32 =
5 IF 1 SPCS +! THEN LOOP DROP ;
• ( И. )
7 CREATE ALPHA 52 ALLOT
8
9 : ALPHACOUNT ( адр счет ~ )
10 ALPHA 52 0 FILL ( очистка массива )
11 0 DO DUP I l С§ 223 AND DUP 64 > OVER 91 < AND
12 IF 65 - 2 • ALPHA * 1 SfAP ♦! ELSE DROP THEN
13 LOOP DROP ;<
14
15
Блок 77 [77 :0]
0 ( Глава 8. Упражнения 1. 06 из 06 )
1 ( 12. )
2 : LETTERBAR ( n с ~) SfAP 0 DO DUP EMIT LOOP DROP ;
3
4 : ALPHAPLOT ( - ) ALPHA CR 26 0 DO DUP I 2 • I
5 § ?DUP 0= 0= IF I 65 I LETTERBAR CR THEN
6 LOOP DROP QUIT ;
7(13)
8 : .S ( - ) DEPTH ?DUP 0=
9 IF ." Stack empty"
10 ELSE 0 DO DEPTH ROLL DUP . LOOP
11 THEN ;
12
13
14
15
300
Блок 78 (78 :Oj
Бло! 81 [81 :0]
( Глава 8. Упражневия 2. 01 из 03 )
( 1. )
: .ARR ( адр n ~) 2 • CR 0 DO DOP I + 9 .
( 2. ) 2 +L00P DROP ;
: .SQARR ( адр о - ) 2 • CR DVP 0
DO DUP 0 DO 0V8R 1 J + + 9 .
2 +L00P CR 2 +L00P 2DR0P ;
( 3. ) : D.ARR ( адр n --)
4 • CR 0 DO DUP I + 29 D. 4 +LOOP DROP ;
: D.SQARR ( адр n - ) 4 ' CR DUP 0
DO DUP 0 DO OVER I J + + 29 D.
4 +LOOP CR 4 +LOOP 2DROP ;
( Может показаться более естественным использовать
LOOP i 2 * 1лн 4 * для вычислевия адреса извлека¬
емого элемента, но быстрее использовать +LOOP м
депап умножение толио раэ в начале опясаняя. )
Блок 79 [79 :0]
( Глава 8. Упражненяя 2. 02 яэ 03 )
( <• )
: P-C ( -- ) CR 201 0
DO 1 6 .R 1 32 - 5 9 */ 6 .R CR 10 +L00P ;
( Заметьте, что мтя шаг цякла равен 10, предел цикла
должев только на 1 превоскодить верхнпо ступеньку.)
( 5. )
: F-C1 ( - ) CR 0 200
D0 1 6 .R I 32 - 5 9 •/ 6 .R CR -10 +L00P ;
( 6. )
: FINDCHAR ( лдр1 с - адр2 ) 1024 0
D0 0YER 0YER SIAP 1 + C6 IF DR0P I + LEAVE THEN
L00P ;
Блок 80 [80 :0]
( Глава 8. Упрахнення 2. 03 нз 03 )
I 7. )
: \ ( $адр1 $адр2 - f ) 0YER 0VER С§
SIAP C# = 0= IF 0 DR0P DR0P EXIT THEN 1*
SIAP lf DUP 1- С§ 0
D0 0VER 0VER 1 + С§ SIAP 1 + С§ = 0=
lF DR0P DR0P 0 LEAVE THEN
L00P DUP lF DR0P DR0P 1 ( или -1 для Форт-83) THEN ;
( 8. )
: SEARCH ( адр $адр - f ) lf DUP 1- C# 0
DO OVER OVER I + С§ SIAP I + С§ = 0=
IF DROP DROP 0 LEAVE THEN
ЧООР DUP IF DROP DROP 1 ( или -1 ) THEN ;
: $FIND ( адр (адр — адр ) 1024 0
DO SIAP 11 SIAP OVER OVER SEARCH IF LEAVE THEN
LOOP DROP ;
0 ( Глава 8. Упрахнення 3. 01 нз 02 )
1 (i. >
2 : .4PICI ( nl n2 n3 n4 — n2 n3 n4) >R >R >R . R> R> R> ;
3 ( Do-loop нельзя пряменять, так как там яспольэуется
4 стек возвратов )
5 ( 2. )
6 : DUP1 ( n - n n ) >R R9 R> ;
7 ( 3. )
8 : J1 R> R> R> R9
9 SVAP >R SVAP >R SVAP >R ;
10 ( 4. )
11 : I R> R> R> R> R> R9
12 SVAP >R SVAP >R SVAP >R SVAP >R SVAP R ;
13 ( 5. )
14 : 1' R> R> R> R> R9
15 SVAP >R SVAP >R SVAP >R SVAP >R ;
Блок 82 [82 :0]
0 ( Глава 8. Упражневяя 3. 02 яэ 02 )
1 ( 6. )
2 : NEILBAVB R> R> DROP R9 >R >R ;
3 7. )
4 ( Выаолням R> R> DROP, чтобы записать новое чясло, а
5 затем ясполням >R >R . )
6 ( 8. )
7 < 1 10 = IF R> DROP 15 >R THEN )
8 9. )
9 : +INDEX ( n - ) R> SVAP R> + >R >R ;
10 ( 10. )
11 ( Воэнякает бесконечныя цякл, так как индекс всегда
12 увеличивается до того, как он будет уменьшев оператором
13 LOOP. Таким образом индекс никогда ве достигвет
14 предела.)
15
Блок 83 [83 :0]
0 ( Глава 8. Упражвевие 4. 01 иэ 02 )
1 i i. >
2 VARIABLE N 1000 N !
3 : P0PSIZE CR 501 0
4 D0 N § 200 / N +! I lf 50 M0D 0=
5 IF I lf .м Day MU. N § .м Size м U. CR THEN
6 L00P 1000 N ! ;
7 ( Население не мохет увеличиваться менее чем на 200 )
8 ( 2. )
9 1000 N !
10 : P0PD0UBLE CR N § 2 * 10000 0
11 D0 N § 200 / N f! DUP N в <=
12 IF .м Doubled by DAY м II U. DR0P LEAVE THEN
13 L00P ;
14
15
301
Bnoi 84 [84 :0]
Блок
87 [87 :0]
( Глава 8. Упражнения 4. 02 из 02 )
0 I
Глава 8. Упрахнения 5. 03 из 03 )
( 3. )
1 I
6. )
: GROITH CR 2001 0
2 :
Y/N .- (Y/N) ? ”
DO N § 200 / 30000 N в - 30000 •/ N f! I lf 100 MOD
3
BEGIN KEY DUP 78 = OVER 89 = OR 0=
0= IF I lf .” Day м U. N § .” Size ” U. CR THEN
4
IHILE DROP
LOOP ;
5
REPEAT 78 = IF 1 ." N" ELSE 0 . V
( 4. )
6
VARIABLE SCALE 500 SCALE !
7
: PLOT ( n - ) SCALE § / 0 DO ." x" LOOP ;
8
: PLOTGROITH CR 2001 0
9
DO N § 200 / 30000 N § - 30000 V N f! I lf 100 MOD
10
0= IF I lf ." Day " 4 .R 3 SPACES N § PLOT CR THEN
11
LOOP ;
12
13
14
15
Bnoi 85 [85 :0]
Бло1 88 [88 :0]
Глава 8. Упражнения 5. 01 is 03 )
I. ) : NEI.S ( - ) DEPTH
0= IF ." Stack emply! '' EXIT THEN DEPTB >R BEGIN
DEPTB ROLL DUP . R> I- >R R* 0= UNTIL R> DROP ;
( 2. )
: ST-SUM ( - ) DEPTH 0= IF 0 EXIT THEN
0 BEGIN + DEPTB 1 = UNTIL ;
: ST-SUM1 ( - ) DEPTH 0= IF 0 EXIT TBEN
DEPTH 1 = IF EXIT THEN
DEPTH 1- 0 DO ♦ LOOP ;
( 3. )
VARIABLE TOTAL 500 TOTAL !
: ?YEAR1 ( — ) CR
BEGIN DUP 4 .R TOTAL в 10 / DUP 6 .R TOTAL +! 1+
TOTAL 6 DUP 6 .R CR 1000 >
UNTIL 500 TOTAL ! ;
Блок 86 [86 :0]
Глава 8. Упражнения 5. 02 из 03 )
<• )
VARIABLE /MlCE 1000 #UICE !
: MICE ( -- ) fMlCK 0 2 • 0
BEGIN #MICE 9 200 / #UICE +! 1+ OVER #UICE в <=
UNTIL SIAP DROP CRt. .” Days needed to double." ;
: MICELOOP ( - ) fMICE в 2 • 10000 1
DO fMICE в 200 / #HICE ♦ !
DUP /MICE в <= IF DROP I LEAVE TBEN
LOOP CR . ." Days needed to double.” ;
(5.)
: GET$ 0 PAD C!
# BEGIN KEY DUP 13 = 0=
WHILE DUP EMIT PAD Св lf DUP PAD C! PAD f C!
REPEAT DROP ;
0 ( Глава 9. Упрахнения 1. 01 из 04 )
1 j 1. )
2 : NEITYPE ( адр n - ) 0 DO DUP I f Св EMIT LOOP DROP ;
3 ( 2. )
4 VARIABLE NEISPAN 0 NEISPAN !
5 : NEIEXPECT ( адр n - ) 0 NEISPAN ! SIAP
6 BEGIN OVER NEISPAN 6 > 0=
7 IF DROP DROP EXIT ELSE KEY DUP 13 <> TNEN
8 WHILE DUP 8 = (если backspace ... )
9 IF NEISPAN § 0 = ( если NEISPAN равен 0, игнорируем)
10 lF DROP ELSE -1 NEISPAN f! EMIT THEN
11 ELSE DUP EMIT OVER NEISPAN § f С! 1 NEISPAN f!
12 THEN
13 REPEAT DROP DROP DROP ;
14 ( Из цикла do-loop не просто осуществить условный переход
15 туда, куда надо и выйти, так как мы это здесь сделали.)
Блок 89 [89 :0]
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
02 из 04
Глава 9. Упрахнения 1.
3. )
.TEXT ( адр - ) 0
BEGIN OVER OVER f C# 0= 0= IHILE lf REPEAT lf TYPE ;
В Форт-83 вы мохете просто выдать адр SPAN § TYPE )
t 4. ) CREATE $SPACE 258 ALLOT
( оставляет 2 байта доя нулей ...)
: GET$ ( - $адр ) |SPACE lf 255 OVER OVER BLANK ." ?
OVER OVER EXPECT -TRAILING |SPACE С! 1- ;
( В Форт-83 это описание будет иметь вид: )
: 83GET$ ( - $адр ) $SPACE lf 258 ." ? "
EXPECT )SPACE DUP SPAN в SIAP С! ;
302
Блок 90 [90 :0]
( Глава 9. Упражнения 1. 03 о 04 )
1 5. ]
: ADD* ( - (адр) (SPACE Св 255 < IF (SPACE DUP Св
4- 255 (SPACE Св - 1+ 0TBR OYER BLANK ." ? " OVER
OVER EXPECT -TRAILING (SPACE Св + $SPACE С! DROP
(SPACE ElSE ." Not enough room to add string ”
|SPACE EXIT TBEN ;
< *• )
: ADD(1 ( - (адр ) |SPACE Св 255 <
IF (SPACE DUP Св + 255 (SPACE Св - 1+ OVER OVER
BLANI ." ? " OVER OTER EXPECT -TPAILING (SPACE Св
+ DUP 254 > IF DROP DROP (SPACE EXIT THEN
(SPACE С! DROP (SPACE
ELSE .” Not enou{h rocm to idd string! " (SPACE
EXIT THEN ;
Блок 91 [91 :0]
( Глава 9. Упражнения 1. 04 i3 04 )
i 7. )
: !DOLLAR$ ( d ~) SIAP OTER DABS <| | | 46 HOLD
IS 36 HOLD ROT SIGN /> DUP (SPACE С!
(SPACE 1+ SIAP CMOVE ;
( »• )
: lEFT( ( (адр 1 n - (адр2 ) OTER Св MIN DUP
ROT 1+ PAD 1+ ROT CMOVE PAD С! PAD ;
Бло! 92 [92 :0]
( Глава 9. Упражненн 2. 01 иэ 02 )
( 1. ) ( следует лн нам заботиться
о начальном обнулении длины ? )
: VARIABLE ( n - ) CREATE ALLOT ;
( 2. )
: (! ( (адр1 (адр2 -- ) OVER Св 1+ CMOVE ;
( 3. )
: (GET ( адр - ) 34 IORD SIAP OVER Св !+ CMOVE ;
( 4. )
: (" ( -- (адр ) 34 IORD PAD OVER Св 1+ CMOVE PAD ;
5. )
( (" входная строжа” (SPACE (! , напрммер, решает
стоящую задачу.)
{ 6. ) CREATE CHOCES (1 , (2 . (3 , (4 , (5 ,
(6 . (7 , (I , (9 . (10 , )
: .(CHOICE ( о - ) 1- 2 • CHOICES + в COUNT TYPE ;
Бло! 93 [93 :0]
0 ( Глава 9. Упражнениа 2. 02 иэ 02 )
1 ( 7. )
2 20 (VARIABLE lSTRING 20 (VARIABLE 2STRING
3 20 (VARIABLE 3STRlNG 20 (VARIABLE 4STRlNG
4 CREATE NSTRING lSTRING , 2STRING . 3STRINC , 4STRlNC .
5 : PARSE( TIB 80 BLANK 0 >IN ! ." ? " QUERY 4 0 DO
6 35 IORD I 2 • NSTRlNC ♦ в (! LOOP TIB 80 BLANK ;
7 ( 8. )
8 : PARSE(1 TlB 80 BLANK 0 >IN ! ." ? ” QUERY 4 0 DO 35
9 IORD >IN в . I 2 • NSTRlNG + в /! LOOP TIB 80 BLANK ;
10 ( >IN увеличивается оператором IORD по мере раэбора )
11 ( 9. ) ( входного потока . )
12 : (( 41 IORD DROP ; IMMEDIATE
13 (( Это действительно работает? Если да, то все это будет
14 проигнорировано и не будет дано сообщения об ошибке )
15 : TEST (( Nor iiIl this. ) ;
Блок 94 [94 :0]
0 ( Глава 9. Упражнения 3. 01 из 03 )
1 j 1. )
2 : NEILEFT( ( (адр1 n - (адр2 ) 1 SIAP MID( ;
3 : NEIRIGHT( ( (адр1 n - (адр2)
4 ( 2. ) OVER Св OVER - U SIAP MID( ;
5 : (CHAR ( с - (адр ) I PAD С! PAD 1+ С! PAD ;
6 | 3. | : lSTCHAR ( (адр - с ) 1+ Св ;
8 : (SIAP ( (адр1 (адр2 --) DUP DUP Св 1+ PAD SIAP CMOVE
9 OVER SIAP OVER Св 1+ CMOVE PAD SIAP OVER Св 1+ CMOVE ;
10 : (SIAP1 ( (адр1 (адр2 --) DUP PAD (! OVER SIAP (!
11 PAD SIAP (! ;
12 ( Преимущество MMSFORTH-пакета операторов для работы со
13 строками очевидно. )
14 I 5. )
15 ( LEFT( RICHT( CHR( ASC (XCHG )
Бло! 95 [95 :0]
0 ( Глава 9. Упражнения 3 02 иэ 03 )
1 ( 6. )
2 : BUIlD( ( (адр - ) >R
3 BEGIN INKEY( DUP DUP (. (“ /" (COMPARE
4 ПНЕ Кв SIAP (+ Re (!
5 REPEAT R> DROP ;
6 ( 7. )
7 : BUILD(1 ( (адр - ) IN( SIAP (! ;
8 ( 8. ) ( Но здесь не используется f )
9 ( Опшпяте 30 CONSTANT SIZE и используйте SIZE всюду, где
10 используется 10 в STOREPHONE , GETPHONE и т.д. Преиму-
11 щество заключается в легюсти изменения размера словаря)
12 ( 9. )
13 : SHOIPHONES ( - ) CR 10 ( или SIZE ) 0
14 DO I 0 PHONES (. 2 SPACES I 1 PHONES (.
15 I AVAILABLE + Св 0= IF LEAVE ELSE CR THEN lOOP ;
303
Блок 96 [96 :0]
Блок 99 [99 :0]
( Глава 9. Упрахнения 3. 03 из 03 )
( io. )
: ERASEALL ( ~)
." Are you sure you want to do this (Y/N)?"
IEY 69 IF 0 0 PHONES 600 0 FILL AVAILABLE 10 0 FlLL
ELSE ." Not done."
THEN ;
Блок 97 [97 :0]
( Глава 9. Упрахнения 4. 01 из 03 )
( 1. )
: $IN PAD 1+ 255 OVER OVER BLANK .” ?“ 0VER 0VER
EXPECT -TRAILING PAD С! 1- ; ( используется нихе )
: D#1N1 ( - d ) 0 0 |IN CONVERT DROP ;
: D#lN2 ( - d ) BEGIN |IN DUP 0 0
ROT CONVERT 4 ( 3 в Форт-83 ) ROLL lf =
WHILE .” REDO M DROP DROP REPEAT ;
: D#IN3
BEGIN $lN DUP lf Ce 45 = DUP >R
IF lf THEN
DUP 0 0 ROT CONVERT ROT ROT R>
IF DNEGATE THEN 4 ( 3 в Форт-83 )
ROLL lf 4 ( 3 в Форт-83 ) ROLL =
WHILE .M REDO " DROP DROP
REPEAT ;
Блок 98 [98 :0]
( Глава 9, Упрахнения 4. 02 из 04 )
1 2. )
: fIN. ( - D ) или ( - d )
BEGIN tIN DUP lf C# 45 = DUP >R
IF lf THEN
DUP 0 0 ROT CONVERT ROT ROT R>
IF DNEGATE THEN
4 ( 3 для Форт-83 ) ROLL lf 4 ( 3 ) ROLL DUP C6
46 = 0= >R = WHILE R> DROP ." REDO " DROP DROP
REPEAT R> IF DROP THEN ;
0
Глава 9. Упрахнения 4.
03 из 03 )
1 (
3. )
2 :
(IN3 ( -- о ) tlN NUMBER DROP ;
3 (
4. )
4 :
INFIX ( - D ) 32 IORD NUMBER DROP ;
5 (
Это словло упрощает последупцне описаниа.
6 :
PLUS ( Dl -- o2 )
INFIX ♦ ;
7 (
5. )
8 :
TIMES ( Dl - n2 )
INFIX ♦ ;
9 :
DlYlDEDBY ( Dl - o2 )
INFIX / ;
10 :
MINUS ( Dl - n2 )
lNFlX - ;
11 :
EQUALS ( n - ) . ;
12
13
14
15
Блок
100 [100 :0]
0 Глава 10. Упрахнения 1. 01 из 01 )
1 1. )
2 Приведенное здесь описание продолхает работу после
3 загрузки спедупцего блока. Предшествуадее описание
4 этого не делает и остаток блока будет проигнорирован.
5 ( 2. )
6 : N-> 0 >IN ! BLK « ." Block ” . ." loaded”
7 CR 1 BLK *! ;
8 ( 3. )
9 : LISTS ( nl o2 - ) OVER ♦ SIAP
10 DO CR CR ." Block " I DUP . LIST CR CR CR CR LOOP ;
11 ( 4. )
12 : л ( -- ) CR .” Block " BLK • . >IN в 64 /MOD
13 .” Line “ . .“ Character " 2 - . CR ; IMMEDIATE
l4j5.)
15 ( FORGET TASK 20 LOAD )
Enoi 101 [101 :0]
0 ( Глава 10. Упрахнения 2. 01 из 02 )
1 ( 1. )
2 : SLOAD ( n - ) DUP 2 MOD
3 IF ." Can't load odd blocks" ABORT THEN LOAD ;
4 ( нлн )
5 ( : SL0AD1 ) ( n - ) ( DUP 2 MOD
6 ABORT" Can’t load odd blocks" LOAD ; )
7 ( 2. )
8 : SLIST ( D - ) DUP 2 MOD t LIST ;
9 ( 3. )
10 : SVIEV ( n - ) DUP 2 MOD 0= - ( \ в Форт-83 ) LIST ;
11 ( 4. )
12 : SLISTS ( nl n2 -- ) 2 • SIAP DUP 2 MOD + SIAP OVER +
13 SIAP DO CR CR ." Block ” I DUP . LIST CR CR CR CR
14 2 +L00P ;
15
304
Bnoi 102 [102 :0]
( Глава 10. Упражяеяяя 2. 02 о 02 )
( 5. )
: SVIEIS ( п1 o2 ~) 2 • SIAP DUP 2 H0D 0= -
( + в Форт-83 ) SIAP OTBR + SIAP DO CR CR
." Block " I DUP . LIST CR CR CR CR 2 ШОР ;
( 6. )
: S-> 0 >IN ! BLK +! ; lMMEDlATE
Бло! 103 [ 103 :0]
( Глава 10. Упражнения 3. 01 яз 02 )
( I- )
100 CONSTANT PACFORTH
( 2. )
: GET FIND 2t в LOAD ; ( В Форт-79 )
: GET1 1 >B0DY • LOAD ; ( В Форт-83 )
( 3- )
: L0AD1T ( адр - ) DUP • 0
DO DUP I 2 • ♦ 2 + 6 LOAD LOOP DROP ;
( *• )
: SH0IBL0CKS ( адр - ) DUP • 0
D0 DUP I 2 • + 2 + в . L00P DR0P ;
Блок 104 [104 :0]
( Глава 10. Упражненяя 3. 02 яз 02 )
|s.)
: 7BL0CKS ( адр - ) CR DUP • 0
DO DUP I 2 • ♦ 6 SHOIBLOCKS CR LOOP DROP ;
( 6. )
: LOADEM ( адр n - ) 2 * 2 + § LOADIT ;
Bnoi 105 [105 :0]
0 ( Глава 10. Упражнеиня 4. 01 яз 03 )
1 ( 1. ) : .LINE ( nl n2 -->
2 SIAP BLOCK SIAP 64 ♦ + 64 -TRAILING TYRE ;
3 ( 2. )
4 : NEIINDEX ( nl n2 - ) OVER + SIAP
5 DO CR I . I 0 .LlNE LOOP ;
6 ( 3. )
7 : NEILIST ( n - ) 16 0
8 DO CR DUP I DUP 2 .R SPACE .LlNE LOOP DROP ;
9 ( 4. )
10 : BLLINE < nl n2 - ) SIAP BLOCK SIAP 64 * +
11 64 32 FILL UPDATE ;
12 ( 5. )
13 : NEITL ( nl n2 - ) lf SIAP
14 DO CR SCR 6 I DUP 2 .R SPACE .LlNE LOOP ;
15 : NEIlISTl ( n -- ) SCR ! 0 15 Tl ;
Enoi 106 [106 :0]
0 ( Глава 10. Упражяеяия 4. 02 яз 03 )
1 ( 6. )
2 : CLEAR-BLOCK ( n - ) BLOCK 1024 32 FILL UPDATE ;
3 ( 7. )
4 : PP ( nl n2 - ) SIAP BLOCK SIAP 64 ’ ♦ DUP 64 32 FILL
5 0 IORD 1+ DUP 1- Ce -TRAILUFG ROT SIAP CMOVE UPDATE ;
6 ( 8. )
7 : NEICOPY ( nl n2 ~) SIAP BLOCK SIAP BLOCK 1024
8 ( 9. ) CMOVE UPDATE ;
9 : <COPIES ( nl n2 n3 -- ) 0
10 DO OVER OVER SIAP I ♦ SIAP I ♦ NEICOPY LOOP DROP DROP ;
11
12
13
14
15
Enoi 107 [107 :0]
0 ( Глава 10. Упражнения 4. 03 яз 03 )
1 ( 10. )
2 : COPIES> ( nl n2 n3 - ) 0
3 DO OVER OVER SIAP Г I - + 1- SIAP I1 1 - ♦ 1- NEICOPY
4 LOOP DROP DROP ; ( но Г таие не стандартное слово )
5 ( 11. )
6 : COPIES ( nl n2 n3 - ) ROT ROT OVER OVER <
7 IF ROT COPIES> ELSE ROT <COPIES TBEN ;
8
9
10
11
12
13
14
15
305
Блок 108 [i08 :0]
Блок 111 [111 :0]
( Глава 10. Упражнения 5. 01 нз 05 )
( 1. )
: ARRAYPUT ( адр n - ) DUP lf ARRLEN § • 1024 >
lF ." Block overflov! м ABORT
8LS8 STORBLK • BLOCK SIAP ARRLEN § * f ARRLEN
• CMOTB UPDATE THEN ;
( 2. )
CREAT8 lARRAY 1 , 2 , 3 , 4 , 5 .
CR8ATE 2ARRAY 6 , 7 , 8 , 9 , 0 ,
: PUTARRAYS ( адр1 адр2 n - ) DUP lf 20 • 1024 >
IF .м Block overflov! м ABORT
ELSE 20 * >R SIAP
STORBLI I BLOCI R§ f 10 CMOVE
STORBLI § BLOCI R> f 10 f 10 CMOVE
UPDATE THEN ;
Блок 109 [109 :0]
Глава 10. Упражнения 5. 02 нз 05 )
3.) 700 CONSTANT PATBLOCI ( Измените это, еслн нужно)
: PD ( nl n2 n3 n4 - ) DUP lf 2 • 1024 >
IF .м Block oferrun** ABORT THEN 2 • >R
PATBLOCK 2f BLOCI R§ f ! UPDATE
PATBLOCI lf BLOCI R§ f ! UPDATE
PATBLOCI BLOCI R> f ! UPDATE ;
: SD ( -- n ) DUP lf 2 • 1024 >
IF .M Block overrun" ABORT THEN 2 • >R
CR R§ 2 / ." Patient nunber H
CR R§ PATBLOCK BLOCI f § ." Weight ”
CR R§ PATBLOCI lf BLOCI f § .M Systolic pressure ''
CR R§ PATBLOCK 2 f BLOCI f § ." Diastolic pressure "
. CR ;
Блок 110 [110 :0]
( Глава 10. Упражнения 5. 03 нз 05 )
( 4. )
VARIABLE CNT
: SUMBLOCK ( о - d ) 0 CNT ! BLOCK 0 0 ROT 512 0
DO DUP 1 2 • + • DUP
lF 1 CNT +! THEN
SIAP >R 0 D+ R>
LOOP DROP ;
( 5- )
: ATE ( nl - n2 ) SUMBLOCK CNT • 0 D/ DROP ;
( в. )
: *D ( n -- ) PATBLOCI BLOCI 0 2 • 2+ DUP 1+ 1024 >
lF ." Block oferrun" ABORT
ELSE PATBLOCI BLOCI DUP >R ♦ ! 1 R> ! UPDATE
THEN ;
0 ( Глава 10. Упраменн 5. 04 ia 05 )
1 J 7. )
2 : DD ( n - ) 2 • 2+
3 PATBLOCI BLOCI DUP >R OTER ♦ DUP 2- ROT 1024 SIAP -
4 CMOTK -1 R> +! UPDATE ;
5 ( I. )
t : SB ( n - 4 ) BLOCI DUP >R 0 0 ROT R> • 0
7 DO DUP 1 2 • + 2+ • SIAP >R 0 D+ R> LOOP DROP ;
8 : AT ( nl - n2 ) DUP SB ROT BLOCI • 0 D/ DROP ;
9 ( ». )
10 : SBS ( n - i ) BLOCI DUP >R 0 0 ROT R> • 0
11 DO DUP 1 2 * ♦ t ♦ • SfAP >R 0 D+ R> LOOP DROP ;
12 : ATS ( nl - n2 ) DUP SBS ROT BLOCI « 0 D/ DROP ;
13
14
15
Блок 112 [112 :0]
0 ( Глава 10. Упражяеян 5. 05 яа 05 )
1 ( 9 продола. )
2 : ADS ( n - J PATBLOCI BLOCI в 2 • 6 <• DUP 1024 2/ >
3 IF ." Block overrun” ABORT
4 ELSE PATBLOCK BLOCI DUP >R + ! 1 R» ♦ !
5 PATBLOCI SBS R> 2+ 2! UPDATE
в THEN *
7 : DDS (’а -- ) 2 ♦ в + PATBLOCI
8 BLOCI DUP >R OTER + DUP 2- ROT 1024 SIAP - CMOVE
t -1 R> + PATBLOCI SBS PATBLOCI BLOCI 2+ 2! UPDATE ;
10 ( 10. )
11 : CHISUM ( nl - n2 ) BLOCI 0
12 1024 0 DO OTER I t в + 2 HOOP SIAP DROP ;
13 : 7BLI= ( nl n2 - *лаг ) CHKSUM SIAP CHISUM = ;
14
15
Бло! 113 [113 :0]
0 ( Глава 10. Упрагнем 6. 01 пз 02 )
1 < '• > . >
2 : BLOCKIORD ( nl n2 n3 - (aMr )
3 BLK • >R >IN § >R ROT BLK ! SIAP >IN !
4 IORD DUP Св PAD SIAP 1+ CMOTE PAD R> >IN ! R> BLK !
5 ( 2. )
6 TARIABLE POSITION 0 POSITION !
7 : B10RD ( nl n2 - $addr )
8 BLK • >R >IN • >R SfAP BLK ! POSITION • >IN !
9 fORD DUP Св PAD SfAP 1+ CMOTE >IN § POSITION !
10 R> >IN ! R> BLK ! PAD ;
11 ( 3. )
12 : BLOCKNUMBER ( nl n2 - n3 ) BIORD NUMBER DROP ;
13
14
15
306
Блок 114 [114 :0]
I Глава 10. Упражнения 6. 02 из 02 )
I 4. )
: T0BL0CI ( nl в2 n3 -- ) >R OVER SfAP BLOCK
P0S1TI0N 6 f >R lf OTER С« R> OVER OVER f >R SIAP
CMOVE C# 1+ POSITION f! R> lf R> SIAP 1- С! UPDATE ;
( Изменение POSITION полезно, r.i. оно позволяет
размещать слова в блоке последовательно с
минимальными издержками контроля. )
Блок 115 [115 :0]
( Глава 10. Упражнения 7. 01 из 02 )
(i.)
: DATA-AVE ( первый последний n — средн. )
ROT R0T lf SIAP >R >R >R 0 0 R> R> R> 0VER 0VER
- >R D0 DUP I METFILE DATALOC в SIAP >R 0 Df R>
LOOP .S DROP R> 0 D/ DROP ;
( 2. )
: TEMP-AVE ( первЬй последний — средн. )
lf SIAP >R >R 0 0 R> R> OVER OVER - >R
DO 1 TEMP « 0 Df LOOP R> 0 D/ DROP ;
( 3. ) ‘
: CORRECT-TEMP ( начало последний — ) lf SIAP
DO 1 TEMP 1 SIAP f! LOOP ;
Блок 116 [116 :0]
( Глава 10. Упражнения 7. 02 из 02 )
( <• )
: PUTFlLE ( адр^айла адр-даннык — ) DUP # lf 1
DO OVER OVER SIAP 1 1- 1024 • f SIAP
I 2 * в BLOCK 1024 CMOVE UPDATE
LOOP ;
( 5. )
: SAVEREC ( nl n2 n3 n4 rec# - ) DEPTH 5 <
lF .” Irong nunber of argunent" ABORT
ELSE METFILE RECLOC >R R§ 6 f ! R6 4 f ! R© 2f !
R> ! THEN ;
117 [117 :0]
Глава 10. Упражнения 8. 01 нз 02 )
1. ) VARIABLE DELIMITER 32 DELIMITER !
CETIORD ( адр n - PAD ) TIB PAD
100 f 60 CMOVE BLK ( Сохранение содержимого буфера)
6 >R >lN # >R TlB 80 0 FILL ( восстановление позднее)
f TlB 64 CMOVE 0 BLK ! 0 >lN !
DELIMITER в lORD DUP С« lf PAD SIAP CMOVE PAD
PAD 100 f TIB 80 CROVE R> >IN ! R> BLK ! ;
2. ) VARIABLE POSITION 0 POSITION !
FILEIORD ( адр - PAD ) TIB PAD 100 f 80 CMOVE
BLK 6 >R >IN • >R TIB 80 0 FILL
POSITION 6 f TIB 64 CMOVE 0 BLK ! 0 >IN !
DELIMITER • IORD DUP C« lf PAD SIAP CMOVE PAD DUP
C# lf POSITION f!
PAD 100 f TIB 80 CMOVE R> >IN ! R> BLK ! ;
118 [118 :0]
Глава 10. Упражнения 8. 02 of 02 )
3. )
FILENUMBER ( адр - n ) FILEIORD NUMBER DROP ;
4- )
TOFILE ( адр1 адр2 - ) DUP >R POSITION в f SIAP DUP
Св DUP POSITION f! SIAP lf ROT ROT CMOVE
DELIMITER в POSITION в R> f С! 1 POSITION f! ;
119 [119 :0]
Глава 10. Упражнения 8. 01 из 01 )
1- )
NEXTFIELD ( - ) ADDFILE FILEIORD DROP ;
2. )
NEXTREC ( - ) 4 0 DO NEXTFIELD LOOP ;
3. )
FINDPHONE ( - ) PAD SEARCHt $! 0 POSITION !
SEARCH-NAME 3 0
DO NEXTFIELD LOOP ADDFILE FILEIORD COUNT TYPE ;
4. )
DELREC ( - ) POSITION в ADDFILE f NEXTREC
POSITION в DUP >R ADDFILE f SIAP
FlNDEOF POSITION в 4 f R> - CMOVE ;
Блок
:i
2 :
3
4
5
6
7
8 (
9 :
10
11
12
13
14
15
Блок
:i
2 :
3 (
4 :
5
6
7
8
9
10
11
12
13
14 ,
15
Блок
0 (
1 (
2 :
3 (
4 :
5 (
6 :
7
8
9 (
10 :
11
12
13
14
15
307
Enoi 120 [120 :0]
Блок 123 [123 :0]
Глава 11. Упраиеян 1. 01 аз 02 }
1. )
2TARIABLE ( - ) CREATR 0 , 0 . ;
2TARlABLK < о - ) CREATE . , в в ;
Последнее опнсанме не является стандартным )
2. )
: $CONSTANT ( - ) CREATE 34 VORD DUP С« 1+ ALLOT ;
( Строка перепоснтся по адресу HERE с помощью VORD
i резерв1руетс( памш для поля параметров
прошзводного слова. )
( з. )
: RESERTE ( n ~) CREATE DUP HERE SIAP
ALLOT SVAP 0 flLL ;
( 4. )
: BLOCIARRAY ( n -
CREATE , 1024 ALLOT
Блок 121 [121 :0]
Глава 11. Упражнения 1. 02 аз 02 )
5. )
: GETBLOCI ( адр ~) DUP § BLOCK SIAP 2+ 1024 CMOVE
( 2+ необходимо, чтобы обойти откомпилированный
номер блока.)
PUTBL0CI ( адр -) DUP § BLOCI SIAP 2+ SIAP 1024
CM0V8 UPDATE
•■ )
В§ ( nl адр — n2
nl n2 адр --
SIAP 2 • * §
SIAP 2 • f !
Блок 122 [122 :0]
01 из 04 )
§ SIAP 2+ § SIAP
0
1
2 :
3 (
4 :
5 :
6 :
7 :
8
9 (
10
11
12 (
13
14
15
Глава 11. Упражнении 2. 02 из 04 )
5. )
%C0L0R ( о - ) 100 255 •/ 3 .R .м X м ;
Это слово упрощает следующие три слова. )
CYAN ( о - ) XC0L0R .м Cyan " ;
YELLOI ( о - ) XC0L0R ." YeIlow ” ;
MAGENTA ( о - ) XC0L0R .м Magenta м ;
COLOR ( nl n2 n3 - ) CREATE ROT , SIAP , ,
DOES> >R R§ § CYAN R§ 2i § YELLOI R> 4 + § MAGENTA ;
CYAN, YELLOI и MAGENTA компилируются в таком порядке,
чтобы упростить возврат при исполнении производных
слов. )
6. ) : QUADRATIC ( а b с - ) CREATE ROT , SIAP , ,
DOES> >R DUP DUP * R§ § * SIAP R§ 2+ § * + R> 4 + § t
Заметьте, что коэффициенты компилируются в
порядке i-b-c. )
Блок 124 [124 :0]
0
1
2
3
4 (
7
8
9
10
11
12
13
14
15
Глава 11. Упражнения 2. 03 из 04 )
7. )
M-MENTEN ( nl n2 - ) CREATE SIAP , ,
DOES> >R DUP R§ § • SIAP R> 2f § I / ;
Для получения болипей точности можно применить
масштабирование. )
8. )
" ( nl n2 - n3 ) ?DUP 0= IF DROP 1 ELSE DUP 1 =
IF DROP ELSE OVER SIAP 1- 0
DO OVER * LOOP SIAP DROP THEN THEN ;
Это слово было описано в главе 8 и используется для
упрощения описания P0LYN0M )
Продолжение в следущем блоке )
13
Блок 125 [125 :0]
Глава 11. Упражнения 2.
1. )
: 2C0NSTANT CREATE , , DOES> DUP
( 2. )
: MAIEDATE ( месяц день год — ) CREATE , , ,
DOES> >R R§ § 0 <f | | 47 HOLD DROP DROP
R§ 2 + § 0 f | 47 HOLD DROP DROP
R§ 4 + § 0 | | |> TYPE ;
( 3. )
: COUNTER ( n - ) CREATE , DOES> 1 SIAP f! ;
( Получение значения счетчика командами ' COUNTIT §
в Форт-79 или ’ COUNTIT >BODY § в ФОРТ-83. )
( 4. ) ( Введите производное слово оператора COUNTER
в описание слова; счетчик будет инкрементирорваться
при каждом обращении. Сброс счетчика должен
проводиться при каждом запуске программы. )
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
Глава 11. Упражнения 2. 04 из.04 )
8. продолж. )
P0LYN0M ( nl n2 ... - ) CREATE DEPTH DUP
DO DEPTH ( 1- в Форт-83 ) ROLL , LOOP
DOES> DUP § SIAP 2f SIAP >R >R >R 0 R> R> R> 0
DO OVER OVER I 2 • I § SIAP I " • SIAP >R SIAP >R ♦
R> LOOP DROP DROP ;
Коэффициенты компилируются в указанном порядке. "0"
заносится на дно стека, чтобы получить результат,
который вычислен в цикле do-loop. )
( 9. )
<CONSTANT> ( min max n — ) CREATE , SIAP , ,
DOES> >R R§ § R§ 2+ § Ш R6 4 + § MIN DUP R> ! ;
Заметьте, что мзначение" производного слова скомпили¬
ровано в первую позицию, позволяя использовать
' <name> или ' <name> >B0DY . )
308
Блок 126 [126 :0]
( Глава II. Упрахяенн 3. 01 яэ 02 )
( 1. )
: DARRAY ( в - ) CREATE 4 ' AlLOT )
DOES> SIAP 4 • + ;
( 2. )
: lARRAY ( n - ) CREATE 2 ♦ ALLOT
nOR9> 9IAP I- 9 • 4> *
: lEARRAY ( n - ) CREATE DUP , 2 • ALLOT*
DOES> >R 1- DUP 0 < OVER R§ § 1- > OR *
IP .м Index error " ABORT ELSE 2 • 21 R> I THEN ;
( 3. )
: OCARRAY ( n -) CREATE HERE SIAP
DUP ALLOT 0 FILL DOES> I ;
: OARRAY ( n - ) CREATE HERE SIAP 2 •
DUP ALLOT 0 FILL DOES> SIAP 2 • ♦ ;
Блок 127 [127 :0]
( Глава 11. Упражнения 3. 02 мз 02 )
( 4. )
: PRESERVE ( nl n2.. - ) CREATE DEPTH 0 DO DEPTH
ROLL , LOOP ; ( Используйте 1- ROLL в Форт-83)
( 5. )
: SAVE-TO-RETURN ( nl n2.. - ) CREATE DEPTH DUP , 0
DO DEPTH ROLL , LOOP
( Используйте 1- ROLL в Форт-83 )
DOES> DUP § 0 DO DUP I 2 • 2f I § SIAP LOOP DROP ;
( 6. )
: .IORD >IN § >R CREATE R> >IN ! 32 IORD C§ 1+ ALLOT
DOES> COUNT TYPE ; *
( Занося >lN в стек возвратов, .IORD может проводить
разбор слова дважды. Первый раз формируется заголовок,
где имя используется оператором CREATE. Второй раз
это же имя пересылается в поле параметров слова. )
Блок 128 [128 :0]
Глава 13. Упражнения 1. 01 из 02 )
1. )
: TASK ; Позволяет удалить редактор из словаря при
отладке. DECIMAL гарантирует, что никакая другая
система счисления не будет случайно использована. )
2. )
Это позволяет при редактировании изменить сразу
ряд блоков.)
з. )
Таким образом задержки могут быть легко изменены. )
( 4. ) ( Чтобы позволить с помощью констант определить
задержки и сделать программу более читаемой. )
( 5.*)
: PAGE 27 EMIT 42 EMIT ; ( или 27 42 C0NTR0L PAGE )
: PTC ( ряд столбец - ) 27 EMIT 61 EMIT
SIAP 32 t EMIT 32 t EMIT ;
Блок 129 [129 :0]
0 ( Глава 13. Упражнения 1. 02 из 02 )
1 ( 6. )
2 27 13 C0NTR0L <DLF> 27 14 CONTROL <ADV> 27 15 CONTROL
3 <HMC> ( Чтобы использовать <CXY> достаточно убрать
4 скобки)
5 ( 7. )
6 ( Использование терминальных возможностей, а не их
7 имитации обеспечивает большее быстродействие, более
8 приятную работу и требует меныпей по размеру
9 программы. )
10
11
12
13
14
15
Блок 130 [130 :0]
0 Глава 13. Упражнении 2. 01 из 01 )
1 I 1. )
2 Слова в KEYVECTORS предполагаются отлаженными, IEYD0
3 может быть отлажено путем загрузки и исполнения про-
4 граммы через IEYD0. Если оно не работает, нужно ис-
5 пользовать в IEYD0 слова-подставки. Блок 10 может
6 быть отлажен при испытании EDITCASE и затем, в конце
7 концов, редактора. Здесь снова, если надо, могут
8 использоваться слова-подставки. )
9 ( 2. )
10 : C0NTR0L-CHAR? ( с -- с f ) DUP 27 < 0TER 0 > AND ;
11 : PRlNTABLE-CHAR? ( с - с Г) DUP 31 > 0TER 127 < AND ;
12 : !NSERT-M0DE? ( - f ) l/R в ;
13
14
15
Блок 131 [131 :0]
0 I Глава 14. Упрахвеняя 1. 01 яэ 02 )
1 1. )
2 будет отображено PF PP 00 PP PP 00 10 00 )
3 ( 2. )
4 ( ЭВМ выдаст PP PP PP PP 00 00 PP PP 00 00 PP
5 00 00 10 00 )
6 j 3. )
7 ( I0RD производит обычно разбор по адресу HERE, поэтому
8 обпасть памяти между HERE и следущей используемой
9 зоной, обычно PAD, может использоваться для разбора
10 данных, поступащих с клавиатуры. )
11 ( 4. )
12 : С, ( с -- ) HERE С! 1 DP t! ;
13 : , ( D - ) BBRE ! 2 DP +! ;
14 ( DP, которая xpaaiT указатель словара, завясят от
15 реалязацяя ■ может в вашем Форт быть другой. )
309
Bnoi 132 [132 :0]
Блок 135 [135 :0]
( Глава 14. Упражнения 1. 02 из 02 )
( 5. )
: .HEM ( - ) SP# PAD - U. ;
( SP§ зависит от реализации. ).
( 6. )
: NEIPlCI ( nl - o2) 2 • 'S + в ;
( 2 ♦ SP« 2- ♦ в FORTH-83 )
( 'S [и SP6] зависят от типа версии. 2- необходимо
для описания в Форт-вЗ, так iai там индекс PICK
иачинается с 0)
( 7. )
: NEI.S ( - ) 'S S0 6 OVER OVER = IF ." Stack empty"
DROP DROP ELSE 2- DO I « -2 +LOOP THEN ;
( *• )
: ZERO-STACK ( - ) SPf S0 в SP§ - 2- 0 FILL ;
Блок 133 [133 :0]
( Гпава 14. Упражнения 2. 01 иэ 01 )
( I- )
>BODY
cfa —
pfa)
2+ ;
: >NAME
cfa
— nfa)
6 - ;
>LINK
cfa —
На
2 - ;
: BODY>
pfa
— cfa)
2 - ;
NAME>
nfa —
cfa)
6 I ;
: LlNK>
lfa
— cfa)
2* ;
N>LINK
( nfa —
■и*;
I 4 I ;
L>NAME
lfa -
■ nfa,
|4-;
( Это работает только с версиями Форта, описанными
в этой (ниге. )
( 2. 3. )
.( Bu должны найти ответы на зти вопросы, используя
версию Форта, которой располагаете. )
( 3. )
: C0L0NV0RDS ( адр! адр2 - ) CR 1+ SWAP
DO I • 2526 = ( или другой адрес исполнительной
программы "двоеточие" ) IF I U. THEN LOOP';
Блок 134 [134 :0]
Глава 14. Упражнеяия 3. 01 из 02 )
1- )
Форма использования <n*ne> VORDS, где <ome>
имя юнтекстного словаря. Использование полей
связи и указателей словарей также обычные слова
перед исполнением.)
( 2. )
FORTH VOCABULARY A-V0C A-V0C DEFINITIONS
: A-FIRST .” The first A-VP0C word ” ;
: A-SEC0ND .” The second A-V0C word H ;
: A-THIRD .” The third A-V0C word H ;
: A-LAST .M The last A-V0C word H ;
3; )
( WORDS лии выдает список слов в контекстном словаре)
( 4. )
( Слово из A-V0C не может быть найдено в словаре F0RTH)
0 ( Глава 14. Упражнении 3. 02 из 02 )
1 ( 5. )
2 A-V0C VOCABULARY B-V0C B-V0C DEFINITIONS
3 : B-FIRST .и The first B-V0C word н ;
4 : B-SEC0ND .н The second B-V0C word ” ;
5 : B-THIRD .м The third B-V0C word ” ;
6 : B-LAST M The last B-V0C word ” ;
7 ( Если А^А8Тможет размещаться в B-V0C, тогда
8 ваши словари связаны. В противном случае каждый
9 из них связан только с Фортом. )
10 ( 6. )
11 B-V0C DEFINITIONS
12 : A-LAST .” Another B-V0C definition ” ;
13 ( Выбирите одно из двух описаний A-V0C и B-V0C. )
14
15
Блок 136 [136 :0]
0 ( Глава U. Упражнения 4. 01 из 03 )
1 ( 2. )
2 : HELP CONTEXT § [COMPILE] HELPS FIND ?DUP
3 IF [ FIND HELPS ] LITERAL 0VER U<
4 IF EXECUTE ELSE DR0P .” not in HELP list.”
5 THEN
6 ELSE .M not found. ”
7 THEN CONTEXT ! ;
8
9
10
11
12
13
14
15
Блок 137 [137 :0]
0 ( Гпава 14. Упражнения 4. 02 из 03 )
1 ( 2. )
2 VOCABULARY HELPS HELPS DEFINITIONS
3
4 BLK § 2+ CONSTANT BASEBLI ( указание на первый блок)
5 32 CONSTANT //BLI число статей в блоке )
6 32 CONSTANT LENGTH ( длина статьи )
7
8 : HELPER ( n - ) CREATE ,
9 ( Создает индекс статей HELP)
10 D0ES> § DUP #/BLK / BASEBLI + BL0CK
11 SfAP L8NGTH • ♦ LENGTH -TRAILING TYPE CR ;
12 0 HELPER DUP 1 HELPER DR0P 2 HELPER SfAP 3 HELPER 0VER
13 4 HELPER ROT 5 HELPER - 6 HELPER t 7 HELPER *
14 8 HELPER / --> ( Каждое из слов
15 должно соответствовать статье в блоке данных. )
310
Блок 138 [138 :0]
БлоЕ 141 [141 :0]
( Глава 14. Упрахнения 4. 03 из 03 В след. блоЕе данные)
( 2. продолх. )
P0RTH DEFINITIONS
: HELP [COMPILE] HELPS FIND ?DUP
IF [ FIND HELPS ] LITERAL OVER U<
IF EXECUTE ELSE DROP ." not in HELP list." THEN
ELSE ." not found. "
THEN [COMPILE] FORTH ;
Используйте [’] HELPS вместо [ FIND HELPS ] LITERAL в
Форт-83. [COMPILE] необходимо только в Форт, где
контекстные словари являются словами немедленного
исполнения. )
( Используйте редактор для запоминания описаний;
следупций блоЕ мохет слухнть примером. Эта версия
HELP воспримет описания нз любого числа блоЕов
начиная с BASEBLK. )
БлоЕ 139 [139 :0]
=
stack
D
~ i
i п
)
=
stack
nl n2
—
nl )
=
stack i
nl n2
—
n2 nl
)
=
stack i
nl n2
--
nl
n2
nl
=
stack i
nl п2 n3
--
n2
пЗ
=
subtract i
nl n2
—
n3
)
=
add
nl
n2
—
n3
j
=
multiply i
nl
n2
—
n3
-
divide
nl
n2
—
n3
)
БлоЕ 140 [140 :0]
( Глава 15. Упрахнения 1. 01 из 02 )
(i.)
: 7C0MPIlE CFA -- ) STATE в !F , ELSE EXECUTE THEN ;
( 2. )
: 837COMPILB ( cfi f -) DUP 1 = lF DROP EXECUTE EXIT
THEN STATE О AND 0= IF EXECUTE ELSE , THEN ;
( В Форт-83 FIND пронэводнт no*ci в словаре до ммпн-
ляцмн млн шсполненяя, т.е. во время мнтерпретацн. )
( 3. )
: N' ( - сГа ) 32 10RD FIND 0=
IF DR0P ." Vord not found " ABORT THEN ;
( 4. )
■: ?COMPILE ( cf« --) STATE в IF DUP . , ELSE
EXECUTE TBEN ;
( 5.) ( >IN указывает на точЕу в тексте, откуда про-
долхается интерпретация после того, как слово найдено)
0 Глава 15. Упрахнения 1. 02 из 02 )
1 I 6. )
2 Сброс указателя входного потоЕа вызывает
3 зациЕЛНвание)
4 I 7. )
5 ( ЦиЕЛ будет продолхаться, таЕ как таковы условия
6 во входном потоке )
7 ( 8. )
8 ( Будет отобрахено только 5 , так как 0 во входном
9 потоке остановит интерпретацию. )
10
11
12
13
14
15
Блок 142 [142 :0]
0 Глава 15. Упрахнения 2. 01 нз 02 )
1 I 1, 2. )
2 Исполнительная программа "двоеточие" является одной
3 н той xe для всех слов типа двоеточие. T.o. поле )
4 ( 3. ) ( программы BASE? будет тем xe самым. )
5 ( Используя FIND [ или ’] <name> и DUMP, вы мохете
6 увидеть, что поля параметров содерхат CFA слов опи-
7 сання. Вам следует уметь отслехивать исполнение.)
8 f 4. )
9 ( Если содерхнмое LF в 2DUMMY указывает на lDUMMY,
10 замените его содерхимое так, чтобы оно указывало
11 на то, на что указывает LF слова lDUMMY. )
12 ( 5. )
13 ( Измените CFA в PFA слова 3DUMMY на CFA слова lDUMMY.
14 Чтобы 3DUMMY ничего не делало, занесите CFA слова
15 EXIT в первую позицию поля параметров 3DUMMY. )
Блок 143 [143 :0]
0 Глава 15. Упрахнения 2. 02 нз 02 )
11 e.)
2 Выполним эксперимент н поменяем содерхнмое первой
3 позиции в поле параметров на значение CFA слова,
4 выполнение которого хелательно. )
5
6
7
8
9
10
11
12
13
14
15
311
Блок 144 [144 :0]
( Г пава 15. Упрахнення 3. 01 нз 02 )
( 1. )
: .L0C1 ( - ) BLK 0 . >IN 0 . ; IMMKDlATB
: .L0C2 ( ~ ) BLK 0 . >IN 0 64 / . ; IMMEDIATE
2. )
( Помещаем это мехду [ н ] . )
3. )
( Помещаем это мехду [ ] нлн описываем )
: .SI .S ; IMMEDIATE
( 4. ) ( Вы выйдете нз состояния компиляции н ;
прнведет к ошибке. )
( 5. ) ( Да. [C0MPILE] мохет компилировать обычные
слова, но это избыточно. )
( 6. ) (. Вы мохете обнаружить в стеке 239, ипи ваш Форт
контропнрует прн ннтерпретацнн ошибки н выдаст
сообщение об ошибке.)
Бпок 145 [145 :0]
( Гпава 15. Упрахнення 3. 02 нэ 02 )
( 7. )
CREATE OPERATOR ] I - * / [
: MATH ( nl n2 n3 - ) 1- 2 * OPERATOR + 0 EXECUTE ;
( Понятно, что использование [ н ] явпяется бопее
простым способом формирования исполнительных векторов,
чем применение FIND ипн ’ .)
Блок 147 [147 :0]
0 Глава 15. Упрахнения 5. 01 из 03 )
111.)
2 Ячейка, предаествуицая той, в которой записано чис-
3 по 3, содерхит CFA слова LIT. Первая ячейка в поле
4 параметров будет содерхать CFA слова 7BRANCH. Сразу
5 вслед за ним следует число, абсолютный или относи-
6 тельный адрес слова. Это позволит вам понять, как
7 работает 7BRANCH [с абсолютными или относительными
8 CFA адресами]. После адреса следует CFA слова, ко-
9 торое заносит в стек 1 и за которым следует CFA
10 спова BRANCH . За числом 3 следует CFA слова EXIT. )
11
12
13
14
15
Бпок 148 [148 :0]
0 ( Глава 15. Упрахнения 5. 02 из 03 )
1 ( 2. ) : .CMP CR .м Here ="
2 HERE U. .м Stack = м .S ; IMMEDIATE
3 ( 3. ) HEX
4 : L00K-AT-IF ( f -) .CMP IF 1 .CMP ELSE 2 .CMP THEN
5 .CMP 3 ; DECIMAL
6 ( HERE увеличивается по мере компиляции 7BRANCH, LIT,
7 адресов и чисел. Стек содерхит число, которое эане-
8 сено туда оператором [ н которое проверяется и
9 удаляется оператором ; с цепью проверки сохранности
10 состояния стека]. За зтим числом следует абсолютные
11 нлн относительные адреса передачи управления, ис-
12 пользуемые операторами ELSE и THEN.)
13 ( 4. ) ( TEST выдаст на дисплей 5, так как LIT и 5
14 былн скомпилированы в поле параметров с помощью
15 [ 174 , 5 , ] . )
Бпок 146 [146 :0]
Бпок 149 [149 :0]
Гпава 15. Упрахнення 4. 01 из 01 )
1. )
START-fHERE? BLK 0 [COMPILE] LITERAL ; IMMEDIATE
2. )
- ) PAD DUP 8 EXPECT 1- NUMBER DR0P ;
current rate? " GET/ ; IMMEDIATE
: GET/
: ?RATE
( 3. )
: CONVERTS ( n ~ ) ?RATE LITERAL 100
( 4. )
ENGLAND CR
DENMARK CR
GERMANY CR
6. )
?C0MP
•/
Dollars to pbunds "
Dollars to kroners
Dollars to marks
?RATE LITERAL 100
?RATE LITERAL 100
?RATE LITERAL 100
STATE 0 0= IF
Compile only! " ABORT THEN
7. ) ( TEST1 выполнит DUP . Выполнение TEST с клавиатуры
даст ошибку, так как C0MPILE содерхит ?C0MP . )
0 ( Глава 15. Упрахнения 5. 03 из 03 )
1 ( 5. ) ( Нвхе предполагается, что 3301 равно CFA
2 спова LIT. )
3 : NEILITERAL ?С0МР 3301 , , ; IMMEDIATE
4 : NEIDLITERAL ?C0MP SIAP 3301 , , 3301 , , ;
5 IMMEDIATE
«< «•)
7 ( Нвхе предполагается, что 3265 равно CFA слова
8 7BRANCH н что 3257 равно CFA слова BRANCH. )
9 : NEIIF ?С0МР 3265 , HERE 0 , ; IMMEDIATE
10 : NEIELSE ?С0МР 3257 , HERE 0 , HERE R0T ! ;
11 IMMEDIATE
12
13
14
15
312
Бпок 150 [150 :0]
Блог 153 [ 153 :0]
I Г пава 15. Упрахнеиня 6. 01 яэ 02 )
( 1. ) : TASI ; FIND TASK 0 CONSTANT
C0L0N-ADDR ( ' TASK 0 в 83 ) COlON-ADDR >R
H8R8 RO , 1 ." Vord#0 " EXIT [ CONSTANT 0I0RD
HERE R> , .” Vord|l ” EXIT CONSTANT 1V0RD
( 2. )
: ANYlORD [ 0I0RD . lIORD . ] ;
( 3- )
: MAIEIORD CREATE 1V0RD , 0V0RD , DOES>
SIAP 2 • + 0 EXECUTE ;
( 4. )
: GIVE-NAME,CREATE . DOES> в EXECUTE ;
( 5. )
COLON-ADDR HERE SIAP . ] ." Vord/2 ” EXIT [
: 2IORD [ SIAP , ] ; ( если в стеке храннтся чнсло,
заспанное оператором : . )
Бпок 151 [151 :0]
Глава 15. Упрахнения 6. 02 нз 02 )
1 «• )
Опшите)
: ?BR 3265 , ; : BR 3257 , ; ( или другие адреса )
( Используется вместо COMPILE 7BRANCH н COMPILE BRANCH
в опнсанмах IF н ELSE . )
(7. )
( Опишите следуицее ниже н используйте вместо COMPILE LIT
в описании LITERAL . ) *
: LT 3301 , ; ( Или другой соответствупций адрес.)
0 Глава 15. Упрахнения 7. 02 из 02 ) )
11 в.)
2 LlT увеличивает число в стеке возвратов и
3 удаляет из стека откомпилированное число.
4 Выполнение программы ." делает то xe самое
5 для скомпилированных строк. )
6
7
8
9
10
11
12
13
14
15
Бпок 154 [154 :0]
0 ( Глава 15. Упрахнения 8. 01 из 01 )
1 i i.)
2 : FACTORIAL ( о - ) 1 SIAP 1+ 2 DO I • L00P ;
3 ( 2. )
4 : SHOIASCII ( nl n2 - ) lt SIAP
5 DO I 3 .R 1 SPACE EMIT 3 SPACES LOOP ;
6 : SH0IASC1I1 ( nl n2 - ) SIAP DUP DUP
7 3 .R SPACE EMIT 3 SPACES 1+ SIAP OVER OVER <=
< IF MYSELF ELSE DROP DROP THEN ;
9 ( '3. )
10 : GENERATIONS ( -- n ) 0 1 BEGIN SIAP H SIAP
11 DUP + DUP 2000 > UNTIL DROP ;
12 : RGENERATIONS ( 0 1 - n ) SIAP 1+ SIAP
13 DUP + DUP 2000 < IF MYSELF ELSE DROP THEN ;
14
15
Блок 152 [152 :0]
Глава 15. Упрахнения 7. 01 иэ 02 )
!• )
Адреса являются ячейками в попе параметров, где запо¬
минаются очередные CFA, подлежащие исполнению. )
2. )
Осуществляется переход к завершению 3LEYEL . )*
3. )
Использование R> DROP в 3LEYEL удалит адрес, который
определяет куда будет передано управление после
выполнения слова, что вызовет сбой в работе внутрен¬
него интерпретатора и разрушение системы. )
I 4- >
i IP мохно использовать для получения значения константы)
. 5. )
i В обоих случаях EXIT передает управление в точку,
откуда было осуществлено обращение к слову. )
Бпок 155 [155 :0]
0 Глава 16. Упрахнения 1. 01 нз 03 )
1 1. )
2 Z80 DR0P El NEXT
3 P0P HL NEXT
4 8088 DR0P 5B NEXT
5 POP BX NEXT
6 Z80 DUP El E5 E5 NEXT
7 POP HL PUSH HL PUSH HL NEXT
8 8088 DUP 5B 53 53 NEXT
9 POP BX PUSH BX PUSH BX NEXT
10 Z80 OVER El Dl D5 E5 D5 NEXT
11 POP HL POP DE PUSH DE PUSH HL PUSH DE NEXT
12 8080 OVER 5B 5A 52 53 52 NEXT
13 POP BX POP DX PUSH DX PUSH BX PUSH DX NEXT)
14
15
313
Блок 156 [156 :0]
Блок 159 [159 :0]
Глава 16. Упрахнения 1. 02 из 03 ) 0 ( Глава 16. Упрахнения 2. 02 из 04 )
2. ) ( HEX ASSEMBLER ) 1 ( 1. продолх. ) ( 8088 )
Z80 CREATE TUCK HERE DUP 2- ! 2 CODE DUP BX POP BX PUSH BX PUSH NEXT END-CODE
E1 С, Dl С, E5 С, D5 С, E5 С, NEXT 3
8088 CREATE TUCK HERE DUP 2- ! 4 CODE OVER BX POP CX POP BX PUSH CX PUSH BX PUSH
5B С, 5A С, 53 С, 52 С, 53 С, NEXT ) 5 NEXT END-CODE
3. ) 6 CODE ROT BX POP CX POP DX POP CX PUSH DX PUSH
Z80 CREATE NlP HERE DUP 2- ! E1 С, Dl С, D5 С, NEXT 7 BX PUSH NEXT END-CODE
8080 CREATE NIP HERE DUP 2- ! 5B С, 5A С, 53 С, NEXT ) 8 CODE 2DUP BX POP CX POP BX PUSH CX PUSH BX PUSH
4. ) DECIMAL 9 CX PUSH NEXT END-CODE
ROT = POP HL POP DE EX
SIAP = POP EX
,HL PUSH DE PUSH HL NEXT 10 CODE NIP BX POP CX POP BX PUSH NEXT END-CODE
,HL PUSH HL NEXT) 11
12 CODE TUCK BX POP CX POP BX PUSH CX PUSH BX PUSH
13 NEXT EfHKODE
14 CODE -ROT BX POP CX POP DX POP BX PUSH CX PUSH
15 DX PUSH NEXT END-COODE
Блок 157 [157 :0]
( Глава 16. Упрахнения 1. 03 из 03 )
( 5. )
( -ROT = POP HL POP DE EX (SP),HL PUSH HL PUSH DE NEXT
-ROT = POP АХ POP BX POP CX PUSH BX PUSH CX PUSH АХ NEXT)
( 6. )
( Приращение указателя стека осуществляется командой INC SP
илн эквивалентной.)
Блок 160 [160 :0]
0 ( Глава 16. Упрахнения 2. 03 иэ 04 )
1 ( 2. ) ASSEMBLER DEFINITIONS
2 : PSH АХ PUSH NEXT END-CODE ;
3 ( 3. ) ASSEMBLER DEFINITIONS
4 : PSH3 BX PUSH DX PUSH AX PUSH NEXT END-CODE ;
5 ( 4. )
6 ASSEMBLER DEFINITIONS
7 : ;C NEXT END-CODE ;
8 ( 5. ) FORTH DEFINITIONS
9 CODE GREGS DI PUSH SI PUSH SP PUSH DX PUSH CX PUSH
10 BX PUSH AX PUSH NEXT END-CODE
11 : .REGS GREGS 7 0 DO 8 .R LOOP ;
12 ( 6. )
13 CODE - DX POP AX POP DX AX SUB AX PU$H
14 NEXT END-CODE
15
Блок 158 [158 :0]
Блок 161 [161 :0]
( Глава 16. Упрахнения 2. 01 из 04 )
0
( Глава 16. Упрахнения 2. 04 из 04 )
( 1. ) ( Z80 )
1
1 7a. )
CODE DUP HL POP HL PUSH HL PUSH NEXT END-CODE
2
: ARRAY CREATE II 2* ALLOT ;CODE AX POP AX AX ADD
3
BX AX ADD 2 1 AX ADD AX PUSH NEXT END-CODE
CODE OVER HL POP DE POP DE PUSH HL PUSH DE PUSH
4
( 7b. ) : DARRAY CREATE II 4 * ALLOT ;CODE
NEXT END-CODE
5
AX POP AX AX ADD AX AX ADD BX AX ADD 2 f AX ADD
CODE ROT DE POP HL POP E3 C, ( HL SP EX ) DE PUSH HL PUSH
6
AX PUSH NEXT END-CODE
NEXT END-CODE
7
( 7c.) ( Это требует лишъ 1/3 времени от того, что
CODE 2DUP HL POP DE POP DE PUSH HL PUSH DE PUSH HL PUSH
8
нухно описаниям, в которых использовано DOES> )
NEXT END-CODE
9
: CONSTANT CREATE , ;CODE 2 f BX ADD [BX] PUSH
CODE TUCI HL POP DE POP HL PUSH DE PUSH HL PUSH
10
NEXT END-CODE
NEXT END-CODE
11
( 7d. ) : 2CONSTANTTREATE , , ;CODE 4 | BX ADD
CODE NIP HL POP DE POP HL PUSH NEXT END-CODE
12
[BX] PUSH 2 f BX SUB [BX] PUSH NEXT END-CODE
13
( Эти описания предполагают, что при обращении BX
CODE -ROT HL POP DE POP E3 C, ( HL SP EX ) DE PUSH HL PUSH
14
содерхнт CFA слова и что PFA отстоит от CFA на два
NEXT END-CODE
15
байта. Это соответствует требованиям MMSFORTH. )
314
Блок 162 [162 :0]
( Гпава 16. Упрахнення 3. 01 иэ 01 ) 0
( 1. ) 1
ASSEMBLER DEFINITIONS 2
0 CONSTANT ВС 2 CONSTANT DE 4 CONSTANT HL 6 CONSTANT AF 3
: lZ80ARG ( n - ) CREATE , DOES> 0 SVAP 8 ‘ + C, ; 4
193 lZ80ARG (POP) 197 lZ80ARG (PUSH) FORTH DEFINITIONS 5
( 2. ) 6
CODE IF=DROP AX POP BX POP AX BX CMP - Z 7
IF BX PUSH AX PUSH THEN NEXT END-CODE 8
( 3. ) ( Следупцее слово почти в 3 раза быстрее, чем 2 • . ) 9
CONE 2* АХ POP АХ АХ ADD АХ PUSH NEXT END-CODE 10
( 4. ) 11
CODE 10* 0 | BX МО? 0 | CX МО? АХ POP 10 | DX MOV 12
BEGIN BX INC DX CX ADD АХ BX CMP Z UNTIL CX PUSH NEXT 13
END-CODE 14
15
Блок 163 [163 :0]
Гпава 16. Упрахнення 4. 01 нз 01 ) 0
'• 1 , , 1
LABEL SAVESTACK неиспольз. per. ] POP АХ PUSH BX PUSH CX PUSH 2
DX PUSH [тот же регистр] PUSH RET END-CODE 3
( Применяйте неиспользуемый регистр в вашем Форте для хранения 4
адреса возврата, мторый заносится в crei при обращении t 5
SAVESTACK) 6
( 2. ) 7
CODE PLOTEMIT { nl n2 - ) DX POP CX POP FFOO CALL NEXT 1
END-CODE 9
( з. ) Ю
CODE CETDATA ( nl -- n2 ) АХ POP FF0{ CALL АХ PUSH NEXT 11
END-CODE 12
I *■ ) 13
CODE CETDATA ( nl - n2 ) АХ POP BX PUSH SI PUSH FF00 CALL 14
SI POP ВХ POP АХ PUSH NEXT END-CODE 15
ОГЛАВЛЕНИЕ
ВВЕДЕНИЕ 5
Немного истории 7
Об этой книге 8
Благодарности 9
Глава 1. Что такое Форт? 10
Учимся на практике 10
Упражнения 14
Полезная программа 15
Упражнения 17
Форт...Почему он такой необычный? 18
Глава 2. Стек 22
Что такое стек? 22
Буфер ввода 23
Стек в арифме.тических операциях 23
Упражнения 25
Манипуляции в стеке 26
Упражнения 30
Выводы 31
Глава 3. Память, числа, символьная информация 32
Что такое память? 32
Двоичная запись и основание системы счисления 33
Упражнения 35
Операции с битами 35
Упражнения 36
Положительные, отрицательные числа и числа без знака 37
Упражнения 38
Операции с байтами 38
Упражнения 39
Американский стандартный код для обмена информацией (ASCII) 39
Упражнения 41
Выводы 41
Глава 4. Еще об арифметических операциях 42
Операторы дляработы с небольшими числами 42
Некоторые проолемы операции деления 42
Деление с округлением, деление с отрицательными числами 43
Упражнения 45
Операции с величинами и знаками чисел 45
Упражнения 46
Упражнения 47
Почему используются целые числа? 47
Масштабирование чисел 51
Упражнения 53
Числа двойной длины 54
Упражнения : 56
Смешанные действия с числами одинарной и двойной длины 56
Упражнения 57
Расширение операций над числами с плавающей запятой 57
Упражнения 59
Упражнения , 62
Выводы 63
Глава 5. Ввод и вывод 64
Вывод символов 65
Упражнения 66
Управление экраном дисплея 67
Вывод на печатающее устройство (принтер) : 68
Построение простейших графиков из линий 68
Упражнения 69
Вывод чисел 70
Упражнения 74
Ввод с клавиатуры 74
Выводы 75
Глава 6. Хранение чисел в памяти 76
Создание переменных 76
Упражнения 78
Перемещение и заполнение содержимого массивов 79
Упражнения 80
Переменная, константа и связанные с ними слова 80
Упражнения 82
О векторном исполнении операторов 83
Упражнения 85
316
Еще о массивах и матрицах 86
Упражнения 88
О разном 88
Выводы 89
Глава 7. Операторы сравнения и ветвления 90
Проверка истинности 91
Упражнения 92
Операторы IF..ELSE..THEN 93
Некоторые замечания о структурном программировании 95
Упражнения 95
Прекращение исполнения задания 95
Упражнения '. 97
Множественный выбор ветвления 98
Упражнения 99
Выводы 100
Глава 8. Организация циклов 101
Циклы типа DO-LOOP 101
Упражнения 106
Еще о циклах типа DO-LOOP 107
Упражнения Л 108
Стек возвратов 108
Упражнения 110
Для тех, кто знаком с дифференциальным исчислением 110
Упражнения 110
Циклы с неопределенным числом повторений 110
Упражнения 112
Выводы 113
Глава 9. Символьные строки 114
Строки счетной длины, их ввод с клавиатуры 114
Упражнения 116
Ввод с помощью слова WORD 117
Упражнения 119
Расширенный набор строковых операций в MMSFORTH 120
Телефонный справочник 123
Упражнения 126
Преобразование символьных строк в числа 126
Упражнения Г 128
Выводы 129
Глава 10. Хранение программ и данных 130
Вывод листинга программы и загрузка 131
Упражнения 132
Скрытые блоки 133
Упражнения 133
Загрузка экранов 134
Упражнения 135
Работа с содержимым блоков 136
Упражнения 138
Хранение данных в блоках 138
Упражнения 140
Хранение символьных строк в блоках 140
Упражнения 142
Использование нескольких блоков в качестве файла 142
Упражнения 144
Файлы строковых данных с последовательным доступом 145
Упражнения : 145
Файл адресов 146
Упражнения 148
Выводы 148
Глава 11. Создание слов-определителей 149
Порождающие и порождаемые слова 149
Определяющие слова 150
Упражнения 151
Создание новых определяющих слов 152
Упражнения 153
Определение массивов 154
Упражнения 156
Отвлечение : реализация игры "Жизнь” 156
Прикладная программа на языке Форт для сбора данных 158
Выводы 160
Глава 12. Редакторы Форта 161
Основы редактирования для Форта 161
Использование экранного редактора 168
Глава 13. Программирование на Форт. Стиль 170
Задание на программу 170
317
Закладка фундамента 175
Упражнения 179
Основные положения , 179
Соединение частей в единое целое 182
Упражнения 184
Комментарии 184
Выводы 186
Глава 14. Память Форта, словари и контекстные словари 187
Об использовании памяти в Форте 187
Упражнения 192
Как слова Форта записаны в словаре? 192
Упражнения 195
Контекстные словари 195
Упражнения ; 198
Упражнения 199
Выводы 199
Глава 15. Интерпретация, компиляция и исполнение 200
Интерпретация 200
Упражнения 203
Компиляция .' 204
Упражнения 205
Упражнения 207
Упражнения 210
Упражнения 212
Упражнения.... 213
Исполнение 214
Упражнения 220
Рекурсия 221
Упражнения 222
Выводы ....*. 222
Глава 16. Программирование на Форт-ассемблере 223
Машинная программа 223
Упражнения 226
Форт-ассемблеры 226
Упражнения 228
Как работает ассемблер 229
Упражнения 231
Обращение к другим программам, написанным в машинных кодах 231
Упражнения 234
Выводы 234
ПРИЛОЖЕНИЕ А. Глоссарий 235
Список слов Форта 235
ПРИЛОЖЕНИЕ Б. Глоссарий 270
Терминология 270
ПРИЛОЖЕНИЕ В. Источники информации 282
Системы Форта 282
Публикации 284
ПРИЛОЖЕНИЕ Г. ASCII Коды 286
ПРИЛОЖЕНИЕ Д. Ответы к упражнениям 288
Оглавление 316
Производственное издание
Келли Мэлон, Спайс Николас
Язык программирования Форт
Заведующий редакцией Ю.Г. Ивашов
Редактор Л.Ю. Камочкина
Художественный редактор В.И. Мусиенко
Корректор Т.Л. Кускова
ИБ N2085
Подписано в печать с оригинал-макета 15.03.93 Формат 84x108 1/16 Бумага для множ. аппаратов Гарнитура "Таймс“ Печать офсетная
Усл. печ. л. 33,60 Усл. кр.-отт. 33,81 Уч.-изд. л. 35,30 Тираж 15 00& экз. Изд. № 23085 Зак. №б£9 С-044
Издательство *Радио и связь”. 101000 Москва, Почтамт, а/я 693
Ордена Трудового Красного Знамени Чеховский полиграфический комбинат Министерства печати и информации РФ. 142300 г. Чехов Московской области
Издательство ’’Радио и связь”
и его дочерняя маркетинговая фирма АО ”РиС”
в связи с формированием диллерской сети
ПРИГЛАШАЮТ К СОТРУДНИЧЕСТВУ
по распространению
технической и специальной литературы
издательства,
коммерческие фирмы,
книготорговые организации,
специалистов в области сбыта.
Мы готовы рассмотреть предложения,
связанные с открытием фирменных магазинов издательства
Наш адрес: Москва, 103051, 2-ой Щемиловский пер., д.4/5
телефон и факс: (095) 258-72-57
телекс: 64.411665 RADIO SU
В издательстве ’’Радио и связь”
в 1993 г. выйдут в свет книги:
Мячев A.A., Красавин A.H., Алексеев E.C.
Персональные ЭВМ.
Толковый словарь.
Англо-русский словарь сокращений
Мячев A.A., Алексеев E.C.
Интерфейсы и сети ЭВМ.
Англо-русский толковый словарь
Григорьев В.Л.
Видеосистемы персональных компьютеров
Алимова Т.Л., Лямин Л.В., Петрова Т.Н. и др.
Персональный компьютер для офиса
Паппас K., Мари У.
Микропроцессор 386. Справочник
(пер. с англ.)
Хелд Г.
Справочное руководство по PS/2
(пер. с англ.)
Стобо Дж.
Язык программирования Пролог
(пер. с англ.)
КОМПЬЮТЕРЫ МАРКИ "LAND":
В ЧЕМ СЕКРЕТ НИЗКИХ ЦЕН ?
ЛЭНД
LAND
МЫ САМИ ПРОИЗВОДИМ И САМИ ПРОДАЕМ •
г.Москва, ул.Воронежская, дом 24, строение 2, АО "Лэнд"
телефоны: 398-49-54, 398-49-66
телефакс: 398-49-66
- 100%-ый российский капитал
- торговый центр в Москве
• 1ВМ<овместамые компьютеры
- торговый центр в Сингапуре
MegaPro
ЛЭНД