Броуди. Начальный курс программирования на языке ФОРТ. 1990
Страница 2 из 2
Страница 2 из 2 • 1, 2
Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990
ФОРТ-АССЕМБЛЕР
Форт часто используют в прикладных областях, где от программ требуется высокая скорость выполнения, например, при обработке сигналов информация поступает в реальное время и компьютер должен справляться с ее обработкой. Как правило, вы существенно выигрываете в скорости, если работаете с Фортом, а не с другими языками программирования, но языку Ассемблера он все же в этом отношении уступает. (Вновь создаваемые Форт-процессоры, такие, как NOVIX NC 4000, непосредственно выполняют команды Форта высокого уровня быстрее, чем традиционные процессоры свои машинные команды. Ассемблер, описанный в данном разделе, имеет смысл только для систем, функционирующих на обычных процессорах).
В прикладной программе почти все время выполнения приходится лишь на ее небольшую часть, а именно на так называемые ВНУТРЕННИЕ ЦИКЛЫ. Если ваша программа, написанная на Форте - языке высокого уровня, работает слишком медленно, вы можете значительно ускорить ее выполнение, переписав один или два внутренних цикла на языке Ассемблера.
В большинстве языков высокого уровня нет хороших средств автономного создания программ на ассемблере и соединения их с основной программой. На Форте же это обычный процесс. Определения на ассемблере Форта выглядят почти так же, как и определения высокого уровня. Если тело определения через двоеточие содержит код высокого уровня:
: НОВОЕ-ИМЯ ( код высокого уровня . . .) ;
то тело ассемблерного определения включает команды на языке Ассемблера:
CODE НОВОЕ-ИМЯ ( код на языке ассемблера . . .) END-CODE
(Слово завершения ассемблерного кода варьируется от системы к системе; Стандарт-83 рекомендует END-CODE).
Слово, которое определяется посредством CODE, хранится в словаре так же, как и все остальные, и выполняется или вызывается подобно любому другому слову. Определенное соответствующим образом это слово будет выполняться со значениями из стека, поэтому вы можете передавать ему аргументы так, как если бы оно было определено через двоеточие. На самом деле, выполняя некоторое слово, вы не в состоянии установить, определено ли оно через двоеточие или через CODE (разве что по скорости выполнения).
Лучше всего писать программу на языке высокого уровня. После того как она начала правильно работать, вы можете выявить те участки, на выполнение которых тратится основное время и переписать их в машинных кодах. Повторно откомпилируйте программу, и она будет работать намного быстрее. Альтернативный способ - заранее фиксировать критичные по времени участки - не столь эффективен.
Рассмотрим создание конкретного ассемблера на примере ассемблера 8080. Очевидно, что для каждого процессора должен существовать свой ассемблер и что ассемблер 8080 подходит только для данного процессора. Если вы введете в ваш компьютер приведенный здесь пример на ассемблере, то получите сгенерированный машинный код для 8080, что, собственно, вам и требуется. Но попытавшись полученный код ВЫПОЛНИТЬ, вы потерпите неудачу. Наш пример показывает, как легко писать на Форт-ассемблере, объясняет основные принципы разработки ассемблера и демонстрирует мощь определяющих слов Форта.
Начнем с определяющего слова CODE. Его назначение - создание заголовка словарной статьи, которая при выполнении передаст управление по адресу, содержащему машинный код. Выполнить это проще, чем понять. Вспомните (см. гл.9), что все определения снабжены полем кода, которое указывает машинный код. В определении CODE такой указатель должен указывать поле параметров данного определения:
Таким образом, простое определение слова CODE может иметь вид:
: CODE CREATE HERE HERE 2- ! ;
Теперь нам нужен набор слов, позволяющий осуществлять трансляцию машинных команд в словарь. Для начала выберем несложное слово. Команда процессора 8080 CMA вычисляет дополнение содержимого регистра А. Код этой операции в двоичной системе счисления выглядит так: 00101111. Чтобы транслировать команду, введем следующее определение:
HEX
: CMA 2F C, ;
Еще одна простая команда XCHG обеспечивает обмен содержимым между парами регистров D-E и H-L. Код такой операции: 11101011. Далее мы можем ввести определение:
: XCHG ЕВ С, ;
Подведем предварительные итоги. Мы задали себе синтаксис написания определений машинных команд и предыдущими действиями создали средства их спецификации. Теперь можно ввести следующий текст:
CODE ТЕСТ CMA XCHG . . .
Получено слово ТЕСТ, выполняющее машинные команды CMA и XCHG. Вы можете для проверки этого слова воспользоваться словом DUMP (но ни в коем случае не инициируйте слово ТЕСТ !).
Как заканчивается CODE-определение, мы покажем позднее, а пока вернемся к определению машинных команд. У нас уже определены две команды, состоящие из восьмиразрядного кода операции. Процессор 8080 имеет довольно много команд такого типа. Поэтому нам необходимо слово для определения всех подобных команд (назовем их командами типа 1) 1MI .
: 1MI ( код-операции -- ) CREATE С, DOES> С@ С, ;
Определим с помощью введенного слова следующие команды (первые два определения по-новому создают уже имеющиеся у нас команды):
HEX
2F 1MI СМA ЕВ 1MI XCHG 00 1MI NOP 76 1MI HLT
F3 1MI DI FB 1MI EI 07 IMI RLC 0F 1MI RRC
17 1MI RAL 1F IMI RAR E9 1MI PCHL F9 1MI SPHL
E3 1MI XTHL 27 1MI DAA 37 1MI STC 3F 1MI CMC
C0 1MI RNZ C8 1MI RZ D0 1MI RNC D8 1MI RC
E0 1MI RPO E8 1MI RPE F0 1MI RP F8 1MI RM
C9 1MI RET
Определяющее слово 1MI создает семейство команд, каждую из которых отличает уникальный код операции, но при компиляции все они ведут себя одинаково: их код заносится в словарь. Вновь образованное определение CMA функционально почти не отличается от прежнего определения через двоеточие. Единственное отличие состоит в том, что слово С, заносящее код операции в словарь, находится в части DOES> слова 1MI, а сам код (2F) - в поле параметров слова CMA.
Мы уже определили большую группу команд процессора 8080. Но остальные его команды не так просты. Например, команда ADD дополнительно вносит содержимое заданного регистра в регистр А (сумматор). Для того чтобы на обычном ассемблере 8080 добавить содержимое регистра В к содержимому регистра А, нужно ввести
ADD В
Код операции ADD в двоичной системе имеет вид 10000SSS, где SSS - три бита, используемые для указания задаваемого регистра (S означает источник). Регистр В задается как 000, отсюда "ADD В" в двоичном коде будет выглядеть следующим образом: 10000000.
Аналогично если регистр L задается как 101 (S), то выражение "ADD L" превратится в 10000101. Иными словами, нужный код операции получается при выполнении команды OR над двоичным значением 10000000 (шестнадцатиричное 80) и числом, обозначающим регистр. Определим операцию ADD так:
: ADD ( регистр# -- ) 80 OR С, ;
Самый простой способ занесения номера нужного регистра в вершину стека - задать номера регистров в виде констант:
0 CONSTANT В
1 CONSTANT С
2 CONSTANT D
3 CONSTANT E
4 CONSTANT H
5 CONSTANT L
7 CONSTANT A
(Здесь перечислены все регистры, которые можно использовать в команде ADD). Теперь для занесения в словарь кода операции сложения значений регистра В и сумматора можно написать:
В ADD
Постфиксная запись ненамного усложняет дело, но зато ассемблер становится проще и сохраняется свойство расширяемости, присущее Форту (с помощью макроподстановки).
В некоторых командах, аналогичных ADD, значение регистра задается в трех младших битах. Поэтому имеет смысл для данного класса команд специфицировать свое определяющее слово:
: 2MI CREATE C, DOES> ( регистр# --) С@ OR С, ;
С помощью этого слова можно ввести следующие определения:
80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB
A0 2MI ANA AB 2MI XRA B0 2MI ORA B8 2MI CMP
(2MI функционирует аналогично 1MI, т.е. запоминает уникальный код операции определяемой команды (ребенка) в поле параметров последней. Отличие же заключается в том, что 2MI заставляет команду-ребенка при выполнении логически складывать посредством OR код операции ребенка с номером регистра из стека).
Существует еще один класс машинных команд, содержащих номера регистров в коде операции, но в другом месте. Например, код команды INR (приращение) имеет вид OODDD100 (шестнадцатиричное число 04), где DDD - регистр, подлежащий приращению (D означает "получатель"). Мы можем воспользоваться константами, обозначающими номера регистров, но при этом необходимо осуществить сдвиг на три бита влево, прежде чем команда OR сложит их с кодом операции (сдвиг на три позиции влево эквивалентен умножению на восемь):
: INR ( регистр# --) 8 * 04 OR С, ;
Как и в предыдущем случае, введем определяющее слово:
: 3MI CREATE С,
DOES> ( регистр# --) С@ SWAP 8 * OR С, ;
04 3MI INR
Теперь выражение "С INR" занесет в словарь код операции: 00001100.
С помощью слова 3MI можно специфицировать еще один класс команд, в чем вы убедитесь, посмотрев листинг, приведенный в конце раздела.
Для создания команд остальных типов нам достаточно ввести всего два определяющих слова - 4MI и 5M1. Первое применяется для образования кодов тех операций, которые требуют дополнительно восьмиразрядного литерала, например ADI (непосредственное сложение с А). Второе слово определяет коды операций, требующих дополнительно 16-разрядного литерала. В качестве примера можно привести команды CALL, JMP и подобные им. Команды MOV, MVI и LXI уникальны и поэтому специфицируются индивидуально посредством двоеточия без использования определяющего слова.
Изучая листинг, обратите внимание на то, что в него включены операторы управления, такие, как IF , ELSE , THEN , BEGIN , UNTIL , WHILE и REPEAT.
Это не совсем те слова, с определениями которых вы уже познакомились ранее (где передача управления компилируется посредством высокоуровневых слов Форта), а их версии, созданные только для ассемблера, где передача управления и разрешение адресов, как и в традиционном ассемблере, осуществляются на уровне машинных команд. Однако они обеспечивают вам возможность программирования с использованием формата структур высокого уровня.
Но можно ли компилировать в словарь различные варианты слов IF , THEN и т.д., они ведь в нем смешаются? Конечно, так как команды ассемблера хранятся в контекстном словаре ASSEMBLER, а не в словаре FORTH. Определение CODE в нашем листинге инициирует слово ASSEMBLER, что делает этот контекстный словарь текущим всякий раз, когда мы начинаем CODE-определение.
Интересной особенностью Форт-ассемблера является и его расширяемость. Если в вашей программе имеются повторяющиеся фрагменты, то вы можете вместо них использовать МАКРОКОМАНДЫ. Ниже приводится пример макрокоманды, которая осуществляет "циклический сдвиг содержимого регистра А влево" и затем "добавляет содержимое регистра В":
: SHIFT+ RLC В ADD ;
Заметьте, что, появившись внутри ассемблерного определения, слово SHIFT+ помещает в словарь две команды, составляющие определение этого слова так, как если бы вместо него были введены сами команды:
RLC В ADD
Адрес слова SHIFT+ не компилируется, а само оно при выполнении его кода не вызывается в качестве подпрограммы. Использование макросредств во время выполнения не приводит к каким-либо накладным расходам, поскольку машинные команды после макроподстановки в точности такие же, как и без нее.
Слово NEXT представляет собой одну из макрокоманд, написанных на языке Ассемблера. В нашей системе она определена так:
: NEXT (NEXT) JMP ;
Иными словами, NEXT - это машинная команда, передающая управление по адресу, который оставляет в вершине стека слово (NEXT) . По данному адресу расположен код адресного интерпретатора (о котором речь шла в гл.9). Адресный интерпретатор является ядром Форта и выполняет поочередно все адреса в скомпилированном Форт-определении. Каждое определение через CODE должно заканчиваться инициированием адресного интерпретатора. Следовательно, любое ассемблерное определение должно завершаться словом NEXT. В нашем ассемблере определения также должны иметь в конце слово END-CODE, которое дополнительно восстанавливает контекст.
Ниже приводятся два примера, где используются команды описанного здесь ассемблера:
HEX
CODE X ( n -- n') \ Меняются местами старший и младший байты n
H POP L A MOV H L MOV A H MOV H PUSH
NEXT END-CODE
(Мы пересылаем n из стека в пару регистров HL, регистр L (младшие байты) в регистр А, регистр Н (старшие байты) в L, а А в H, помещаем содержимое пары регистров HL в стек, передаем управление NEXT).
CODE BP ( a # -- ) \ Перевод из нижнего регистра в верхний
D POP H POP
BEGIN D A MOV E ORA 0= NOT WHILE
M A MOV 60 CPI CS NOT IF
20 SUI A M MOV THEN
D DCX H INX REPEAT NEXT END-CODE
(Пересылаем счетчик в пару регистров DE, а адрес - в пару регистров HL, начинаем цикл, проверяем, выполняя команду OR над содержимым регистров D и E, не равно ли значение счетчика нулю. Пока его значение не равно нулю, перемещаем символ из памяти, на которую ссылается указатель, в сумматор. Если код обрабатываемого символа больше 60 (строчная "а" и выше), вычитаем десятичное число 32, преобразуя символ в прописной, и записываем в память. Уменьшаем счетчик и увеличиваем адрес. Повторяем цикл. Передаем управление NEXT ).
Преимущество работы на ассемблере такого вида заключается в том, что вы во время ассемблирования "находитесь в Форте". Если вам необходимо идентифицировать некоторое устройство с помощью имени, а не числа, вы можете определить его как обычную константу и присвоить ей имя внутри ассемблерного определения. Можно воспользоваться определением через двоеточие как макрокомандой или даже обратиться к переменной, поскольку она помещает в вершину стека свой адрес и поэтому может быть задействована в команде "непосредственной загрузки". Применение машинных команд раскрывает перед вами всю мощь языка Форт.
В основу описанного здесь ассемблера положен ассемблер 8080, разработанный Дж.Кэсседи. Мы внесли в него изменения в соответствии со Стандартом-83 и для простоты изучения убрали некоторые зависимые от системы фрагменты. С оригиналом вы можете познакомиться в [1]. Ассемблер для других процессоров описан в [2], [3], [4].
\ Ассемблер 8080
\ учебная версия ассемблера 8080,основанная на фигФорте;
\ разработана Джоном Кэсседи
HEX
VOCABULARY ASSEMBLER
: CODE CREATE HERE HERE 2- ! ASSEMBLER ;
ASSEMBLER DEFINITIONS
: END-CODE CURRENT @ CONTEXT ! ;
0 CONSTANT В 1 CONSTANT С 2 CONSTANT D 3 CONSTANT E
4 CONSTANT H 5 CONSTANT L 6 CONSTANT PSW 6 CONSTANT M
6 CONSTANT SP 7 CONSTANT A
: 1MI CREATE C, DOES> C@ C, ;
: 2MI CREATE C, DOES> C@ OR C, ;
: 3MI CREATE C, DOES> С@ SWAP 8 * OR C, ;
: 4MI CREATE C, DOES> С@ С, С, ;
: 5MI CREATE С, DOES> С@ С, , ;
\ Ассемблер 8080
HEX
00 1MI NOP 76 1MI HLT F3 1MI DI FB 1MI ED
07 1MI RLC 0F 1MI RRC 17 1MI RAL 1F 1MI RAR
E9 1MI PCHL F9 1MI SPHL E3 1MI XTHL EB 1MI XCHG
27 1MI DAA 2F 1MI CMA 37 1MI STC 3F 1MI CMC
80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB
A0 2MI ANA A8 2MI XRA B0 2MI ORA B8 2MI CMP
B9 3MI DAD C1 3MI POP C3 3MI PUSH B2 3MI STAX
0A 3MI LDAX 04 3MI INR 05 3MI DCR 03 3MI INX
0B 3MI DCX C7 3MI RST D3 4MI OUT DB 4MI SBI
E6 4MI ANI ЕЕ 4MI XRI F6 4MI ORI FE 4MI CPI
22 5MI SHLD 2A 5MI LHLD 32 5MI STA 3A 5MI LDA
CD 5MI CALL
\ Ассемблер 8880
HEX
C9 1MI RET C3 5MI JMP C2 CONSTANT 0= D2 CONSTANT CS
E2 CONSTANT PE F2 CONSTANT 0<
: NOT 8 OR ;
: MOV 8 * 40 + + C, ;
: MVI 8 * 6 + C, C, ;
: LXI 8 * 1+ C, , ;
: THEN HERE SWAP ! ;
: IF C, HERE 0, ;
: ELSE C3 IF SWAP THEN ;
: BEGIN HERE ;
: UNTIL C, , ;
: WHILE IF ;
: REPEAT SWAP JMP THEN ;
: NEXT (NEXT) JMP ;
УСОВЕРШЕНСТВОВАННЫЙ ГЕНЕРАТОР БЕССМЫСЛЕННЫХ СООБЩЕНИЙ
Генератор бессмысленных сообщений, о котором шла речь в гл.10, имеет существенный недостаток: необходимо осуществлять возврат каретки в определении слова СООБЩЕНИЕ. Это ведет к мозаичному выводу фраз.
Для того чтобы генерировать несколько сообщений, мы должны поручить самой программе фиксировать моменты, когда требуется возврат каретки. Следующий ниже листинг приводится без всяких комментариев. Приобретайте навыки чтения программ на Форте. Попробуйте сделать определения более ясными. Результат работы нашей программы выглядит так:
В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ
СРЕДСТВА В ИНТЕГРИРОВАННЫЙ ЦИФРОВОЙ КОМПЛЕКС ПРИМЕНЯЯ АВТОНОМНЫЙ КУЛЬТУРНЫЙ
ПРОДУКТ ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА КВАЛИФИЦИРОВАННЫЙ ЦИФРОВОЙ
ПРОЕКТ ЕЩЕ БОЛЬШЕ КРЕПИТЬ УНИКАЛЬНЫЙ ПРОГРАММНЫЙ ОБЪЕМ.
С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО СТРУКТУРИРОВАННО ПРИМЕНЯЯ
ОБЩИЙ ТРУДОВОЙ ОБЪЕМ УЧИТЫВАЯ АВТОМАТИЗИРОВАННЫЙ ПРОГРАММНЫЙ АВТОМАТ
СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ СЛУЧАЙНЫЙ ПРОИЗВОДСТВЕННЫЙ ПРОЦЕСС
ЕЩЕ БОЛЬШЕ КРЕПИТЬ КВАЛИФИЦИРОВАННЫЙ МНОГООТРАСЛЕВОЙ ПРИНЦИП.
С ДРУГОЙ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО
СТРУКТУРИРОВАННО ПРИМЕНЯЯ ЦИФРОВОЙ ХИМИЧЕСКИЙ КОМПЛЕКС УЧИТЫВАЯ НЕОБЫЧАЙНЫЙ
МНОГООТРАСЛЕВОЙ АВТОМАТ НЕОБХОДИМО РАССМАТРИВАЯ НЕОБЫЧАЙНЫЙ ПРОГРАММНЫЙ
КРИТЕРИИ ФУНКЦИОНИРОВАТЬ КАК ВРЕМЕННЫЙ ЦИФРОВОЙ ПРОЕКТ.
В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО СТРУКТУРНО ПРИМЕНЯЯ
СЛУЧАЙНЫЙ КУЛЬТУРНЫЙ ИНТЕРЕС УЧИТЫВАЯ СИСТЕМАТИЗИРОВАННЫЙ ЦИФРОВОЙ УРОВЕНЬ
НЕОБХОДИМО РАССМАТРИВАЯ ЦИФРОВОЙ ТРУДОВОЙ ПРОЕКТ ЕЩЕ БОЛЬШЕ КРЕПИТЬ
СИНХРОНИЗИРОВАННЫЙ КОРОТКОВОЛНОВЫЙ КРИТЕРИИ.
Block# 156
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 VARIABLE ОСТАТОК \ количество оставшихся символов, подлежащих сканированию
2 VARIABLE ПО-ГОРИЗОНТАЛИ \ текущее положение курсора для вывода по
3 \ горизонтали
4 70 CONSTANT ПРАВГРАН \ правая граница
5 : СКАНЕР ( длина-поиска -- адр-пробела | конец-поля)
6 2DUP + ROT ROT OVER + SWAP ВО 1 С@ BL =
7 IF DROP I LEAVE THEN LOOP ;
8 : ДАЙСЛОВО ( a -- a слово-счетчик) \ получ. очереди, слова для форматирования
9 DUP ОСТАТОК @ BUP 0> IF СКАНЕР ELSE DROP THEN OVER -
10 DUP 1+ NEGATE ОСТАТОК +! ;
11 : СЮДА? ( счетчик -- 1=подходит для данной строки)
12 ПО-ГОРИЗОНТАЛИ @ + ПРАВГРАН < ;
13 : SPACE' ПО-ГОРИЗОНТАЛИ @ IF SPACE 1 ПО-ГОРИЗОНТАЛИ +! THEN ;
14 : CR' CR 0 ПО-ГОРИЗОНТАЛИ ! ;
15 157 LOAD 158 LOAD
Block# 157
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 : .СЛОВО ( а # -- ) \ вывод слова, если необходимо, выполнение CR
2 DUP СЮДА? IF SPACE' ELSE CR' THEN
3 DUP ПО-ГОРИЗОНТАЛИ +! TYPE ;
4 : СЛЕДУЮЩЕЕ ( a # -- след.адр.)
5 + 1+ ;
6 : ВЫВОД ( а #символов -- ) \ вывод сформатированного текста
7 ОСТАТОК !
8 BEGIN ДАЙСЛОВО DUP WHILE 2DUP .СЛОВО СЛЕДУЮЩЕЕ REPEAT
9 2DROP ;
10 : АБРЕД ( -- а) 161 BLOCK ; \ случайные слова
11 : ОБОРОТЫ ( -- а) 160 BLOCK ; \ связывающие обороты
12 : ВВЕДЕНИЯ ( - a) 159 BLOCK ; \ начала фраз
13 : БРЕД ( #строки #столбца -- а) \ получение адреса слова
14 20 * SWAP 64 * + АБРЕД + ;
15
Block# 158
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 : .БРЕД ( #строки #столбца -- ) БРЕД 20 ВЫВОД ;
2 : ЧАСТЬ-РЕЧИ ( #строки -- ) CREATE , \ определение частей речи
3 DOES> @ 10 CHOOSE SWAP .БРЕД ;
4 0 ЧАСТЬ-РЕЧИ 1ПРИЛАГАТЕЛЬНОЕ
5 1 ЧАСТЬ-РЕЧИ 2ПРИЛАГАТЕЛЬНОЕ
6 2 ЧАСТЬ-РЕЧИ СУЩЕСТВИТЕЛЬНОЕ
7 : ФРАЗА 1ПРИЛАГАТЕЛЬНОЕ 2ПРИЛАГАТЕЛЬНОЕ СУЩЕСТВИТЕЛЬНОЕ ;
8 : ОБОРОТ ( #группы -- ) [ 4 64 * ] LITERAL *
9 3 CHOOSE 64 * + ОБОРОТЫ + 64 ВЫВОД ;
10 : ПОВЕСТВОВАНИЕ 4 0 DO I ОБОРОТ ФРАЗА LOOP ." ." CR' ;
11 : ВВЕДЕНИЕ ( #абзаца -- )
12 CR' 64 * ВВЕДЕНИЯ + 64 ВЫВОД ;
13 : СООБЩЕНИЕ CR' CR' 4 0 DO I ВВЕДЕНИЕ ПОВЕСТВОВАНИЕ LOOP ;
14
15
Block# 159
0 В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО
1 С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО
2 С ДРУГОЕ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО
3 В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО
4
5
6
7
8
9
10
11
12
13
14
15
Block# 160
0 ПРИМЕНЯЯ
1 ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ СРЕДСТВА В
2 СТРУКТУРИРОВАННО ПРИМЕНЯЯ
3
4 ИМЕЯ В ВИДУ
5 ЧТОБЫ КОМПЕНСИРОВАТЬ
6 УЧИТЫВАЯ
7
8 ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА
9 СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ
10 НЕОБХОДИМО РАССМАТРИВАЯ
11
12 ФУНКЦИОНИРОВАТЬ КАК
13 СОЗДАТЬ
14 ЕЩЕ БОЛЬШЕ КРЕПИТЬ
15
Block# 161
_0 ВЫСОКИЙ_____________КУЛЬТУРНЫЙ__________УРОВЕНЬ
_1 ОБЩИЙ_______________ПРОИЗВОДСТВЕННЫЙ____ИНТЕРЕС
_2 АВТОМАТИЗИРОВАННЫЙ__НАУКОЕМКИЙ__________КОМПЛЕКС
_3 ЗАПЛАНИРОВАННЫЙ_____ВАЛОВОЙ_____________ОБЪЕМ
_4 ИНТЕГРИРОВАННЫЙ_____ЦИФРОВОЙ____________КОЭФФИЦИЕНТ
_5 КВАЛИФИЦИРОВАННЫЙ___МНОГООТРАСЛЕВОЙ_____ПРИНЦИП
_6 ПРЕДСТАВИТЕЛЬНЫЙ____ХИМИЧЕСКИЙ__________ГЕНЕРАТОР
_7 ТЕХНОЛОГИЧЕСКИЙ_____НЕПРЕРЫВНЫЙ_________ПРОЦЕСС
_8 АВТОНОМНЫЙ__________АППАРАТНЫЙ__________ИНТЕРФЕЙС
_9 ЦИФРОВОЙ____________НЕЗАВИСИМЫЙ_________АВТОМАТ
10 СИНХРОНИЗИРОВАННЫЙ__ФУНКЦИОНАЛЬНЫЙ______КРИТЕРИЙ
11 СИСТЕМАТИЗИРОВАННЫЙ_КОРОТКОВОЛНОВОЙ_____ПРОЕКТ
12 СЛУЧАЙНЫЙ___________ОТРИЦАТЕЛЬНЫЙ_______ИМПУЛЬС
13 НЕОБЫЧАЙНЫЙ_________ТРУДОВОЙ____________ПОДЪЕМ
14 ВРЕМЕННЫЙ___________НЕХАРАКТЕРНЫЙ_______СПАД
15 УНИКАЛЬНЫЙ__________ПРОГРАММНЫЙ_________ПРОДУКТ
УПРАЖНЕНИЯ
1. Для описания четырех полей в простой файловой системе мы применяли следующие определения:
( смещение) ( длина)
CREATE фамилия 0 , 16 ,
CREATE имя 16 , 12 ,
CREATE работа 28 , 24 ,
CREATE телефон 52 , 12 ,
а затем добавили еще одно
64 CONSTANT /ЗАПИСЬ
чтобы определить длину всей записи. Изменяя длину поля с фамилией, мы должны также подправлять начальный адрес остальных трех полей, а также значение переменной /ЗАПИСЬ . Найдите программный способ изменения этих значений. Определите синтаксис и напишите программу.
2. Используя язык базы данных, реализованной в простой файловой системе, определите новое слово "вызвать", которое осуществляло бы поиск имени и помещало в вершину стека ФИО и номер телефона, например:
вызвать Конни
_Конни_Чанг_555-9653_ok
ЛИТЕРАТУРА
1. Cassady. John J., "8080 Assembler," Forth Dimensions, III/6, p.180.
2. Duncan, Ray, "Forth 8086 Assembler," Dr.Dobb's Journal, 09/05, pp.28-35, May 1984
3. Perry, Michael A., "A 68000 FORTH Assembler," Dr.Dobb's Journal, 08/09, pp.28-43, September 1983.
4. Ragsdale, William F., "A FORTH Assembler for the 6502," Dr.Dobb's Journal, 06/09, pp.12-24, September 1981; reprinted in Forth Dimensions, III/5, pp.143-50, January/February 1982.
Форт часто используют в прикладных областях, где от программ требуется высокая скорость выполнения, например, при обработке сигналов информация поступает в реальное время и компьютер должен справляться с ее обработкой. Как правило, вы существенно выигрываете в скорости, если работаете с Фортом, а не с другими языками программирования, но языку Ассемблера он все же в этом отношении уступает. (Вновь создаваемые Форт-процессоры, такие, как NOVIX NC 4000, непосредственно выполняют команды Форта высокого уровня быстрее, чем традиционные процессоры свои машинные команды. Ассемблер, описанный в данном разделе, имеет смысл только для систем, функционирующих на обычных процессорах).
В прикладной программе почти все время выполнения приходится лишь на ее небольшую часть, а именно на так называемые ВНУТРЕННИЕ ЦИКЛЫ. Если ваша программа, написанная на Форте - языке высокого уровня, работает слишком медленно, вы можете значительно ускорить ее выполнение, переписав один или два внутренних цикла на языке Ассемблера.
В большинстве языков высокого уровня нет хороших средств автономного создания программ на ассемблере и соединения их с основной программой. На Форте же это обычный процесс. Определения на ассемблере Форта выглядят почти так же, как и определения высокого уровня. Если тело определения через двоеточие содержит код высокого уровня:
: НОВОЕ-ИМЯ ( код высокого уровня . . .) ;
то тело ассемблерного определения включает команды на языке Ассемблера:
CODE НОВОЕ-ИМЯ ( код на языке ассемблера . . .) END-CODE
(Слово завершения ассемблерного кода варьируется от системы к системе; Стандарт-83 рекомендует END-CODE).
Слово, которое определяется посредством CODE, хранится в словаре так же, как и все остальные, и выполняется или вызывается подобно любому другому слову. Определенное соответствующим образом это слово будет выполняться со значениями из стека, поэтому вы можете передавать ему аргументы так, как если бы оно было определено через двоеточие. На самом деле, выполняя некоторое слово, вы не в состоянии установить, определено ли оно через двоеточие или через CODE (разве что по скорости выполнения).
Лучше всего писать программу на языке высокого уровня. После того как она начала правильно работать, вы можете выявить те участки, на выполнение которых тратится основное время и переписать их в машинных кодах. Повторно откомпилируйте программу, и она будет работать намного быстрее. Альтернативный способ - заранее фиксировать критичные по времени участки - не столь эффективен.
Рассмотрим создание конкретного ассемблера на примере ассемблера 8080. Очевидно, что для каждого процессора должен существовать свой ассемблер и что ассемблер 8080 подходит только для данного процессора. Если вы введете в ваш компьютер приведенный здесь пример на ассемблере, то получите сгенерированный машинный код для 8080, что, собственно, вам и требуется. Но попытавшись полученный код ВЫПОЛНИТЬ, вы потерпите неудачу. Наш пример показывает, как легко писать на Форт-ассемблере, объясняет основные принципы разработки ассемблера и демонстрирует мощь определяющих слов Форта.
Начнем с определяющего слова CODE. Его назначение - создание заголовка словарной статьи, которая при выполнении передаст управление по адресу, содержащему машинный код. Выполнить это проще, чем понять. Вспомните (см. гл.9), что все определения снабжены полем кода, которое указывает машинный код. В определении CODE такой указатель должен указывать поле параметров данного определения:
Таким образом, простое определение слова CODE может иметь вид:
: CODE CREATE HERE HERE 2- ! ;
Теперь нам нужен набор слов, позволяющий осуществлять трансляцию машинных команд в словарь. Для начала выберем несложное слово. Команда процессора 8080 CMA вычисляет дополнение содержимого регистра А. Код этой операции в двоичной системе счисления выглядит так: 00101111. Чтобы транслировать команду, введем следующее определение:
HEX
: CMA 2F C, ;
Еще одна простая команда XCHG обеспечивает обмен содержимым между парами регистров D-E и H-L. Код такой операции: 11101011. Далее мы можем ввести определение:
: XCHG ЕВ С, ;
Подведем предварительные итоги. Мы задали себе синтаксис написания определений машинных команд и предыдущими действиями создали средства их спецификации. Теперь можно ввести следующий текст:
CODE ТЕСТ CMA XCHG . . .
Получено слово ТЕСТ, выполняющее машинные команды CMA и XCHG. Вы можете для проверки этого слова воспользоваться словом DUMP (но ни в коем случае не инициируйте слово ТЕСТ !).
Как заканчивается CODE-определение, мы покажем позднее, а пока вернемся к определению машинных команд. У нас уже определены две команды, состоящие из восьмиразрядного кода операции. Процессор 8080 имеет довольно много команд такого типа. Поэтому нам необходимо слово для определения всех подобных команд (назовем их командами типа 1) 1MI .
: 1MI ( код-операции -- ) CREATE С, DOES> С@ С, ;
Определим с помощью введенного слова следующие команды (первые два определения по-новому создают уже имеющиеся у нас команды):
HEX
2F 1MI СМA ЕВ 1MI XCHG 00 1MI NOP 76 1MI HLT
F3 1MI DI FB 1MI EI 07 IMI RLC 0F 1MI RRC
17 1MI RAL 1F IMI RAR E9 1MI PCHL F9 1MI SPHL
E3 1MI XTHL 27 1MI DAA 37 1MI STC 3F 1MI CMC
C0 1MI RNZ C8 1MI RZ D0 1MI RNC D8 1MI RC
E0 1MI RPO E8 1MI RPE F0 1MI RP F8 1MI RM
C9 1MI RET
Определяющее слово 1MI создает семейство команд, каждую из которых отличает уникальный код операции, но при компиляции все они ведут себя одинаково: их код заносится в словарь. Вновь образованное определение CMA функционально почти не отличается от прежнего определения через двоеточие. Единственное отличие состоит в том, что слово С, заносящее код операции в словарь, находится в части DOES> слова 1MI, а сам код (2F) - в поле параметров слова CMA.
Мы уже определили большую группу команд процессора 8080. Но остальные его команды не так просты. Например, команда ADD дополнительно вносит содержимое заданного регистра в регистр А (сумматор). Для того чтобы на обычном ассемблере 8080 добавить содержимое регистра В к содержимому регистра А, нужно ввести
ADD В
Код операции ADD в двоичной системе имеет вид 10000SSS, где SSS - три бита, используемые для указания задаваемого регистра (S означает источник). Регистр В задается как 000, отсюда "ADD В" в двоичном коде будет выглядеть следующим образом: 10000000.
Аналогично если регистр L задается как 101 (S), то выражение "ADD L" превратится в 10000101. Иными словами, нужный код операции получается при выполнении команды OR над двоичным значением 10000000 (шестнадцатиричное 80) и числом, обозначающим регистр. Определим операцию ADD так:
: ADD ( регистр# -- ) 80 OR С, ;
Самый простой способ занесения номера нужного регистра в вершину стека - задать номера регистров в виде констант:
0 CONSTANT В
1 CONSTANT С
2 CONSTANT D
3 CONSTANT E
4 CONSTANT H
5 CONSTANT L
7 CONSTANT A
(Здесь перечислены все регистры, которые можно использовать в команде ADD). Теперь для занесения в словарь кода операции сложения значений регистра В и сумматора можно написать:
В ADD
Постфиксная запись ненамного усложняет дело, но зато ассемблер становится проще и сохраняется свойство расширяемости, присущее Форту (с помощью макроподстановки).
В некоторых командах, аналогичных ADD, значение регистра задается в трех младших битах. Поэтому имеет смысл для данного класса команд специфицировать свое определяющее слово:
: 2MI CREATE C, DOES> ( регистр# --) С@ OR С, ;
С помощью этого слова можно ввести следующие определения:
80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB
A0 2MI ANA AB 2MI XRA B0 2MI ORA B8 2MI CMP
(2MI функционирует аналогично 1MI, т.е. запоминает уникальный код операции определяемой команды (ребенка) в поле параметров последней. Отличие же заключается в том, что 2MI заставляет команду-ребенка при выполнении логически складывать посредством OR код операции ребенка с номером регистра из стека).
Существует еще один класс машинных команд, содержащих номера регистров в коде операции, но в другом месте. Например, код команды INR (приращение) имеет вид OODDD100 (шестнадцатиричное число 04), где DDD - регистр, подлежащий приращению (D означает "получатель"). Мы можем воспользоваться константами, обозначающими номера регистров, но при этом необходимо осуществить сдвиг на три бита влево, прежде чем команда OR сложит их с кодом операции (сдвиг на три позиции влево эквивалентен умножению на восемь):
: INR ( регистр# --) 8 * 04 OR С, ;
Как и в предыдущем случае, введем определяющее слово:
: 3MI CREATE С,
DOES> ( регистр# --) С@ SWAP 8 * OR С, ;
04 3MI INR
Теперь выражение "С INR" занесет в словарь код операции: 00001100.
С помощью слова 3MI можно специфицировать еще один класс команд, в чем вы убедитесь, посмотрев листинг, приведенный в конце раздела.
Для создания команд остальных типов нам достаточно ввести всего два определяющих слова - 4MI и 5M1. Первое применяется для образования кодов тех операций, которые требуют дополнительно восьмиразрядного литерала, например ADI (непосредственное сложение с А). Второе слово определяет коды операций, требующих дополнительно 16-разрядного литерала. В качестве примера можно привести команды CALL, JMP и подобные им. Команды MOV, MVI и LXI уникальны и поэтому специфицируются индивидуально посредством двоеточия без использования определяющего слова.
Изучая листинг, обратите внимание на то, что в него включены операторы управления, такие, как IF , ELSE , THEN , BEGIN , UNTIL , WHILE и REPEAT.
Это не совсем те слова, с определениями которых вы уже познакомились ранее (где передача управления компилируется посредством высокоуровневых слов Форта), а их версии, созданные только для ассемблера, где передача управления и разрешение адресов, как и в традиционном ассемблере, осуществляются на уровне машинных команд. Однако они обеспечивают вам возможность программирования с использованием формата структур высокого уровня.
Но можно ли компилировать в словарь различные варианты слов IF , THEN и т.д., они ведь в нем смешаются? Конечно, так как команды ассемблера хранятся в контекстном словаре ASSEMBLER, а не в словаре FORTH. Определение CODE в нашем листинге инициирует слово ASSEMBLER, что делает этот контекстный словарь текущим всякий раз, когда мы начинаем CODE-определение.
Интересной особенностью Форт-ассемблера является и его расширяемость. Если в вашей программе имеются повторяющиеся фрагменты, то вы можете вместо них использовать МАКРОКОМАНДЫ. Ниже приводится пример макрокоманды, которая осуществляет "циклический сдвиг содержимого регистра А влево" и затем "добавляет содержимое регистра В":
: SHIFT+ RLC В ADD ;
Заметьте, что, появившись внутри ассемблерного определения, слово SHIFT+ помещает в словарь две команды, составляющие определение этого слова так, как если бы вместо него были введены сами команды:
RLC В ADD
Адрес слова SHIFT+ не компилируется, а само оно при выполнении его кода не вызывается в качестве подпрограммы. Использование макросредств во время выполнения не приводит к каким-либо накладным расходам, поскольку машинные команды после макроподстановки в точности такие же, как и без нее.
Слово NEXT представляет собой одну из макрокоманд, написанных на языке Ассемблера. В нашей системе она определена так:
: NEXT (NEXT) JMP ;
Иными словами, NEXT - это машинная команда, передающая управление по адресу, который оставляет в вершине стека слово (NEXT) . По данному адресу расположен код адресного интерпретатора (о котором речь шла в гл.9). Адресный интерпретатор является ядром Форта и выполняет поочередно все адреса в скомпилированном Форт-определении. Каждое определение через CODE должно заканчиваться инициированием адресного интерпретатора. Следовательно, любое ассемблерное определение должно завершаться словом NEXT. В нашем ассемблере определения также должны иметь в конце слово END-CODE, которое дополнительно восстанавливает контекст.
Ниже приводятся два примера, где используются команды описанного здесь ассемблера:
HEX
CODE X ( n -- n') \ Меняются местами старший и младший байты n
H POP L A MOV H L MOV A H MOV H PUSH
NEXT END-CODE
(Мы пересылаем n из стека в пару регистров HL, регистр L (младшие байты) в регистр А, регистр Н (старшие байты) в L, а А в H, помещаем содержимое пары регистров HL в стек, передаем управление NEXT).
CODE BP ( a # -- ) \ Перевод из нижнего регистра в верхний
D POP H POP
BEGIN D A MOV E ORA 0= NOT WHILE
M A MOV 60 CPI CS NOT IF
20 SUI A M MOV THEN
D DCX H INX REPEAT NEXT END-CODE
(Пересылаем счетчик в пару регистров DE, а адрес - в пару регистров HL, начинаем цикл, проверяем, выполняя команду OR над содержимым регистров D и E, не равно ли значение счетчика нулю. Пока его значение не равно нулю, перемещаем символ из памяти, на которую ссылается указатель, в сумматор. Если код обрабатываемого символа больше 60 (строчная "а" и выше), вычитаем десятичное число 32, преобразуя символ в прописной, и записываем в память. Уменьшаем счетчик и увеличиваем адрес. Повторяем цикл. Передаем управление NEXT ).
Преимущество работы на ассемблере такого вида заключается в том, что вы во время ассемблирования "находитесь в Форте". Если вам необходимо идентифицировать некоторое устройство с помощью имени, а не числа, вы можете определить его как обычную константу и присвоить ей имя внутри ассемблерного определения. Можно воспользоваться определением через двоеточие как макрокомандой или даже обратиться к переменной, поскольку она помещает в вершину стека свой адрес и поэтому может быть задействована в команде "непосредственной загрузки". Применение машинных команд раскрывает перед вами всю мощь языка Форт.
В основу описанного здесь ассемблера положен ассемблер 8080, разработанный Дж.Кэсседи. Мы внесли в него изменения в соответствии со Стандартом-83 и для простоты изучения убрали некоторые зависимые от системы фрагменты. С оригиналом вы можете познакомиться в [1]. Ассемблер для других процессоров описан в [2], [3], [4].
\ Ассемблер 8080
\ учебная версия ассемблера 8080,основанная на фигФорте;
\ разработана Джоном Кэсседи
HEX
VOCABULARY ASSEMBLER
: CODE CREATE HERE HERE 2- ! ASSEMBLER ;
ASSEMBLER DEFINITIONS
: END-CODE CURRENT @ CONTEXT ! ;
0 CONSTANT В 1 CONSTANT С 2 CONSTANT D 3 CONSTANT E
4 CONSTANT H 5 CONSTANT L 6 CONSTANT PSW 6 CONSTANT M
6 CONSTANT SP 7 CONSTANT A
: 1MI CREATE C, DOES> C@ C, ;
: 2MI CREATE C, DOES> C@ OR C, ;
: 3MI CREATE C, DOES> С@ SWAP 8 * OR C, ;
: 4MI CREATE C, DOES> С@ С, С, ;
: 5MI CREATE С, DOES> С@ С, , ;
\ Ассемблер 8080
HEX
00 1MI NOP 76 1MI HLT F3 1MI DI FB 1MI ED
07 1MI RLC 0F 1MI RRC 17 1MI RAL 1F 1MI RAR
E9 1MI PCHL F9 1MI SPHL E3 1MI XTHL EB 1MI XCHG
27 1MI DAA 2F 1MI CMA 37 1MI STC 3F 1MI CMC
80 2MI ADD 88 2MI ADC 90 2MI SUB 98 2MI SBB
A0 2MI ANA A8 2MI XRA B0 2MI ORA B8 2MI CMP
B9 3MI DAD C1 3MI POP C3 3MI PUSH B2 3MI STAX
0A 3MI LDAX 04 3MI INR 05 3MI DCR 03 3MI INX
0B 3MI DCX C7 3MI RST D3 4MI OUT DB 4MI SBI
E6 4MI ANI ЕЕ 4MI XRI F6 4MI ORI FE 4MI CPI
22 5MI SHLD 2A 5MI LHLD 32 5MI STA 3A 5MI LDA
CD 5MI CALL
\ Ассемблер 8880
HEX
C9 1MI RET C3 5MI JMP C2 CONSTANT 0= D2 CONSTANT CS
E2 CONSTANT PE F2 CONSTANT 0<
: NOT 8 OR ;
: MOV 8 * 40 + + C, ;
: MVI 8 * 6 + C, C, ;
: LXI 8 * 1+ C, , ;
: THEN HERE SWAP ! ;
: IF C, HERE 0, ;
: ELSE C3 IF SWAP THEN ;
: BEGIN HERE ;
: UNTIL C, , ;
: WHILE IF ;
: REPEAT SWAP JMP THEN ;
: NEXT (NEXT) JMP ;
УСОВЕРШЕНСТВОВАННЫЙ ГЕНЕРАТОР БЕССМЫСЛЕННЫХ СООБЩЕНИЙ
Генератор бессмысленных сообщений, о котором шла речь в гл.10, имеет существенный недостаток: необходимо осуществлять возврат каретки в определении слова СООБЩЕНИЕ. Это ведет к мозаичному выводу фраз.
Для того чтобы генерировать несколько сообщений, мы должны поручить самой программе фиксировать моменты, когда требуется возврат каретки. Следующий ниже листинг приводится без всяких комментариев. Приобретайте навыки чтения программ на Форте. Попробуйте сделать определения более ясными. Результат работы нашей программы выглядит так:
В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ
СРЕДСТВА В ИНТЕГРИРОВАННЫЙ ЦИФРОВОЙ КОМПЛЕКС ПРИМЕНЯЯ АВТОНОМНЫЙ КУЛЬТУРНЫЙ
ПРОДУКТ ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА КВАЛИФИЦИРОВАННЫЙ ЦИФРОВОЙ
ПРОЕКТ ЕЩЕ БОЛЬШЕ КРЕПИТЬ УНИКАЛЬНЫЙ ПРОГРАММНЫЙ ОБЪЕМ.
С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО СТРУКТУРИРОВАННО ПРИМЕНЯЯ
ОБЩИЙ ТРУДОВОЙ ОБЪЕМ УЧИТЫВАЯ АВТОМАТИЗИРОВАННЫЙ ПРОГРАММНЫЙ АВТОМАТ
СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ СЛУЧАЙНЫЙ ПРОИЗВОДСТВЕННЫЙ ПРОЦЕСС
ЕЩЕ БОЛЬШЕ КРЕПИТЬ КВАЛИФИЦИРОВАННЫЙ МНОГООТРАСЛЕВОЙ ПРИНЦИП.
С ДРУГОЙ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО
СТРУКТУРИРОВАННО ПРИМЕНЯЯ ЦИФРОВОЙ ХИМИЧЕСКИЙ КОМПЛЕКС УЧИТЫВАЯ НЕОБЫЧАЙНЫЙ
МНОГООТРАСЛЕВОЙ АВТОМАТ НЕОБХОДИМО РАССМАТРИВАЯ НЕОБЫЧАЙНЫЙ ПРОГРАММНЫЙ
КРИТЕРИИ ФУНКЦИОНИРОВАТЬ КАК ВРЕМЕННЫЙ ЦИФРОВОЙ ПРОЕКТ.
В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО СТРУКТУРНО ПРИМЕНЯЯ
СЛУЧАЙНЫЙ КУЛЬТУРНЫЙ ИНТЕРЕС УЧИТЫВАЯ СИСТЕМАТИЗИРОВАННЫЙ ЦИФРОВОЙ УРОВЕНЬ
НЕОБХОДИМО РАССМАТРИВАЯ ЦИФРОВОЙ ТРУДОВОЙ ПРОЕКТ ЕЩЕ БОЛЬШЕ КРЕПИТЬ
СИНХРОНИЗИРОВАННЫЙ КОРОТКОВОЛНОВЫЙ КРИТЕРИИ.
Block# 156
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 VARIABLE ОСТАТОК \ количество оставшихся символов, подлежащих сканированию
2 VARIABLE ПО-ГОРИЗОНТАЛИ \ текущее положение курсора для вывода по
3 \ горизонтали
4 70 CONSTANT ПРАВГРАН \ правая граница
5 : СКАНЕР ( длина-поиска -- адр-пробела | конец-поля)
6 2DUP + ROT ROT OVER + SWAP ВО 1 С@ BL =
7 IF DROP I LEAVE THEN LOOP ;
8 : ДАЙСЛОВО ( a -- a слово-счетчик) \ получ. очереди, слова для форматирования
9 DUP ОСТАТОК @ BUP 0> IF СКАНЕР ELSE DROP THEN OVER -
10 DUP 1+ NEGATE ОСТАТОК +! ;
11 : СЮДА? ( счетчик -- 1=подходит для данной строки)
12 ПО-ГОРИЗОНТАЛИ @ + ПРАВГРАН < ;
13 : SPACE' ПО-ГОРИЗОНТАЛИ @ IF SPACE 1 ПО-ГОРИЗОНТАЛИ +! THEN ;
14 : CR' CR 0 ПО-ГОРИЗОНТАЛИ ! ;
15 157 LOAD 158 LOAD
Block# 157
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 : .СЛОВО ( а # -- ) \ вывод слова, если необходимо, выполнение CR
2 DUP СЮДА? IF SPACE' ELSE CR' THEN
3 DUP ПО-ГОРИЗОНТАЛИ +! TYPE ;
4 : СЛЕДУЮЩЕЕ ( a # -- след.адр.)
5 + 1+ ;
6 : ВЫВОД ( а #символов -- ) \ вывод сформатированного текста
7 ОСТАТОК !
8 BEGIN ДАЙСЛОВО DUP WHILE 2DUP .СЛОВО СЛЕДУЮЩЕЕ REPEAT
9 2DROP ;
10 : АБРЕД ( -- а) 161 BLOCK ; \ случайные слова
11 : ОБОРОТЫ ( -- а) 160 BLOCK ; \ связывающие обороты
12 : ВВЕДЕНИЯ ( - a) 159 BLOCK ; \ начала фраз
13 : БРЕД ( #строки #столбца -- а) \ получение адреса слова
14 20 * SWAP 64 * + АБРЕД + ;
15
Block# 158
0 ( Генератор бессмысленных выражений, вариант с самоформатированием )
1 : .БРЕД ( #строки #столбца -- ) БРЕД 20 ВЫВОД ;
2 : ЧАСТЬ-РЕЧИ ( #строки -- ) CREATE , \ определение частей речи
3 DOES> @ 10 CHOOSE SWAP .БРЕД ;
4 0 ЧАСТЬ-РЕЧИ 1ПРИЛАГАТЕЛЬНОЕ
5 1 ЧАСТЬ-РЕЧИ 2ПРИЛАГАТЕЛЬНОЕ
6 2 ЧАСТЬ-РЕЧИ СУЩЕСТВИТЕЛЬНОЕ
7 : ФРАЗА 1ПРИЛАГАТЕЛЬНОЕ 2ПРИЛАГАТЕЛЬНОЕ СУЩЕСТВИТЕЛЬНОЕ ;
8 : ОБОРОТ ( #группы -- ) [ 4 64 * ] LITERAL *
9 3 CHOOSE 64 * + ОБОРОТЫ + 64 ВЫВОД ;
10 : ПОВЕСТВОВАНИЕ 4 0 DO I ОБОРОТ ФРАЗА LOOP ." ." CR' ;
11 : ВВЕДЕНИЕ ( #абзаца -- )
12 CR' 64 * ВВЕДЕНИЯ + 64 ВЫВОД ;
13 : СООБЩЕНИЕ CR' CR' 4 0 DO I ВВЕДЕНИЕ ПОВЕСТВОВАНИЕ LOOP ;
14
15
Block# 159
0 В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО
1 С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО
2 С ДРУГОЕ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО
3 В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО
4
5
6
7
8
9
10
11
12
13
14
15
Block# 160
0 ПРИМЕНЯЯ
1 ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ СРЕДСТВА В
2 СТРУКТУРИРОВАННО ПРИМЕНЯЯ
3
4 ИМЕЯ В ВИДУ
5 ЧТОБЫ КОМПЕНСИРОВАТЬ
6 УЧИТЫВАЯ
7
8 ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА
9 СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ
10 НЕОБХОДИМО РАССМАТРИВАЯ
11
12 ФУНКЦИОНИРОВАТЬ КАК
13 СОЗДАТЬ
14 ЕЩЕ БОЛЬШЕ КРЕПИТЬ
15
Block# 161
_0 ВЫСОКИЙ_____________КУЛЬТУРНЫЙ__________УРОВЕНЬ
_1 ОБЩИЙ_______________ПРОИЗВОДСТВЕННЫЙ____ИНТЕРЕС
_2 АВТОМАТИЗИРОВАННЫЙ__НАУКОЕМКИЙ__________КОМПЛЕКС
_3 ЗАПЛАНИРОВАННЫЙ_____ВАЛОВОЙ_____________ОБЪЕМ
_4 ИНТЕГРИРОВАННЫЙ_____ЦИФРОВОЙ____________КОЭФФИЦИЕНТ
_5 КВАЛИФИЦИРОВАННЫЙ___МНОГООТРАСЛЕВОЙ_____ПРИНЦИП
_6 ПРЕДСТАВИТЕЛЬНЫЙ____ХИМИЧЕСКИЙ__________ГЕНЕРАТОР
_7 ТЕХНОЛОГИЧЕСКИЙ_____НЕПРЕРЫВНЫЙ_________ПРОЦЕСС
_8 АВТОНОМНЫЙ__________АППАРАТНЫЙ__________ИНТЕРФЕЙС
_9 ЦИФРОВОЙ____________НЕЗАВИСИМЫЙ_________АВТОМАТ
10 СИНХРОНИЗИРОВАННЫЙ__ФУНКЦИОНАЛЬНЫЙ______КРИТЕРИЙ
11 СИСТЕМАТИЗИРОВАННЫЙ_КОРОТКОВОЛНОВОЙ_____ПРОЕКТ
12 СЛУЧАЙНЫЙ___________ОТРИЦАТЕЛЬНЫЙ_______ИМПУЛЬС
13 НЕОБЫЧАЙНЫЙ_________ТРУДОВОЙ____________ПОДЪЕМ
14 ВРЕМЕННЫЙ___________НЕХАРАКТЕРНЫЙ_______СПАД
15 УНИКАЛЬНЫЙ__________ПРОГРАММНЫЙ_________ПРОДУКТ
УПРАЖНЕНИЯ
1. Для описания четырех полей в простой файловой системе мы применяли следующие определения:
( смещение) ( длина)
CREATE фамилия 0 , 16 ,
CREATE имя 16 , 12 ,
CREATE работа 28 , 24 ,
CREATE телефон 52 , 12 ,
а затем добавили еще одно
64 CONSTANT /ЗАПИСЬ
чтобы определить длину всей записи. Изменяя длину поля с фамилией, мы должны также подправлять начальный адрес остальных трех полей, а также значение переменной /ЗАПИСЬ . Найдите программный способ изменения этих значений. Определите синтаксис и напишите программу.
2. Используя язык базы данных, реализованной в простой файловой системе, определите новое слово "вызвать", которое осуществляло бы поиск имени и помещало в вершину стека ФИО и номер телефона, например:
вызвать Конни
_Конни_Чанг_555-9653_ok
ЛИТЕРАТУРА
1. Cassady. John J., "8080 Assembler," Forth Dimensions, III/6, p.180.
2. Duncan, Ray, "Forth 8086 Assembler," Dr.Dobb's Journal, 09/05, pp.28-35, May 1984
3. Perry, Michael A., "A 68000 FORTH Assembler," Dr.Dobb's Journal, 08/09, pp.28-43, September 1983.
4. Ragsdale, William F., "A FORTH Assembler for the 6502," Dr.Dobb's Journal, 06/09, pp.12-24, September 1981; reprinted in Forth Dimensions, III/5, pp.143-50, January/February 1982.
Gudleifr- Admin
- Сообщения : 3403
Дата регистрации : 2017-03-29
Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990
ПРИЛОЖЕНИЕ A. ОТВЕТЫ К УПРАЖНЕНИЯМ
ГЛАВА 1
1.
: ДАР ." подставку для книг" ;
: ДАРИТЕЛЬ ." Маша" ;
: БЛАГОДАРНОСТЬ CR ." Дорогая " ДАРИТЕЛЬ ." ," CR
5 SPACES ." спасибо за " ДАР ." . " ;
2.
: МЕНЬШЕ-НА-ДЕСЯТЬ ( n -- n-10 ) -10 + ;
или
: МЕНЬШЕ-НА-ДЕСЯТЬ ( n -- n-10 ) 10 - ;
3. При компиляции определения БЛАГОДАРНОСТЬ компилятор включает в него определение слова ДАРИТЕЛЬ, существующее на момент компиляции. Если вы после компиляции слова БЛАГОДАРНОСТЬ добавите в словарь новый вариант слова ДАРИТЕЛЬ, то этот никак не отразится на уже скомпилированном слове БЛАГОДАРНОСТЬ. (Но вы можете переопределить (перекомпилировать) и слово БЛАГОДАРНОСТЬ. В этом случае в его определение войдет новый вариант слова ДАРИТЕЛЬ).
ГЛАВА 2
ОТВЕТЫ К УПРАЖНЕНИЮ 2-A
1. a b + c * или c a b + *
2. a b * 100 /
3. 3 a * b - 4 / c +
4. a 1 + 4 /
5. 7 x * 5 + x *
6. (a - b) / (b + a)
7. a / 10b
ОТВЕТЫ К УПРАЖНЕНИЮ 2-Б
2.
: 2Б2 ( c a b -- x) 4 * - 6 / + ;
3.
: 2Б3 ( a b -- x) 8 * / ;
4.
: 2Б4 ( a b -- x) * 200 / ;
5.
: 2Б5 ( a a -- x) 2 * 3 + * ;
6. Если вы скажете, что такое выражение преобразовать нельзя, то будете правы, по крайней мере сейчас, пока мы еще не рассмотрели специальных стековых операций.
ОТВЕТЫ К УПРАЖНЕНИЮ 2-B
1. SWAP ROT
2. SWAP DUP ROT SWAP
3.
: 2B3 DUP 1 + SWAP / ;
или
: 2B3 DUP 1+ SWAP / ;
4.
: 2B4 DUP 7 * 5 + * ;
5.
: 2B5 OVER 9 * SWAP - * ;
В КОНЦЕ ГЛАВЫ 2
1.
DUP DUP (1 2 -- 1 2 2 2)
2DUP (1 2 -- 1 2 1 2)
2.
: NIP ( а b -- b) SWAP DROP ;
3.
: TUCK ( а b - Ь a b) SWAP OVER ;
4.
: -ROT ( а b с - cab) ROT ROT ;
5. SWAP 2SWAP SWAP
6.
: 3DUP ( n1 n2 n3 -- n1 n2 n3 n1 n2 n3) DUP 2OVER ROT ;
7.
: 2-7 ( c a b -- n) OVER + * + ;
8.
: 2-8 ( a b -- n) 2DUP - ROT ROT + / ;
9.
: УПАКОВКА ( #яиц --)
12 /MOD . ." коробок и " . ." не упаковано " ;
ГЛАВА 4
1.
-1 0= NOT . _-1_ok
0 0= NOT . _0_ok
200 0= NOT . _-1_ok
2. Не спросит ничего.
3. Употреблять спиртные напитки можно только с 21 года, тогда:
: РАЗРЕШЕНИЕ ( возраст --)
20 > IF ." Употребление алкоголя разрешено "
ELSE ." Вы еще молоды " THEN ;
4.
: ЗНАКИ ( n) DUP 0= IF ." Нуль " ELSE
DUP 0< IF ." Отрицательное " ELSE
." Положительное " THEN THEN DROP ;
Или как-нибудь иначе - лишь бы работало.
5.
: <> ( n1 n2 -- ?) = NOT ;
6.
: XOR ( x y -- ?)
2DUP NOT AND SWAP ROT NOT AND OR ;
7.
: STARS ( n --) ?DUP IF STARS THEN ;
8.
: NEGATE ( n -- -n) 0 SWAP - ;
: ABS ( n - |n| ) DUP 0< IF NEGATE THEN ;
9.
: /UP ( делимое делитель -- частное) /MOD SWAP IF 1+ THEN ;
10.
: -ROT ( a b c -- c a b) ROT ROT ;
: WITHIN ( n l h -- ?) -ROT OVER > NOT -ROT > AND ;
Ниже приводится более эффективный вариант, в котором используются приемы, рассмотренные в следующих главах:
: WITHIN ( n l h -- ?) OVER - >R - R> U< ;
11.
: УГАДАЙ ( ответ попытка -- ответ )
2DUP = IF ." Вы угадали! " 2DROP ELSE
2DUP < IF ." Слишком много " ELSE
." Слишком мало " THEN DROP THEN ;
12.
: .ОТРИЦАТЕЛЬНОЕ ( n -- |n|) 0< IF ." Отрицательное " ABS THEN ;
: ПРОПИСЬ ( n --) DUP ABS 4 > IF ." Выходит за границу " ELSE
DUP .ОТРИЦАТЕЛЬНОЕ DUP 0= IF ." Нуль " ELSE
DUP 1 = IF ." Один " ELSE
DUP 2 = IF ." Два " ELSE
DUP 3 = IF ." Три " ELSE
." Четыре "
THEN THEN THEN THEN THEN DROP ;
13. В предположении, что -ROT и WITHIN уже загружены:
: 3DUP ( a b c -- a b c a b c) DUP 2OVER ROT ;
: ЛОВУШКА ( ответ -меньш-число -большее-число -- ответ | --)
3DUP OVER = -ROT = AND
IF ."Вы угадали! " 2DROP DROP ELSE
3DUP SWAP 1 + SWAP WITHIN IF ." Между "
ELSE ." Вне " THEN 2DROP THEN ;
ГЛАВА 5
1. -1 интерпретируется как число "отрицательная единица"; 1- является словом форта, которое вычитает единицу из значения на стеке.
2. */ NEGATE
3. МАХ МАХ МАХ .
4. а)
: 2ЗНАЧ ( n1 n2 -- n? n?) \ помещение большего значения на вершину
2DUP > IF SWAP THEN ;
б)
: 3ЗНАЧ ( n1 n2 n3 -- n? n? n?) \ большее значение на вершину
2ЗНАЧ >R 2ЗНАЧ R> 2ЗНАЧ ;
Вы можете продолжать в том же духе и далее...
: 4ЗНАЧ \ иэ четырех элементов стека больший поместить в вершину
3ЗНАЧ >R 3ЗНАЧ R> 2ЗНАЧ ;
... последним оператором во всем случаях должен быть 2ЗНАЧ .
в)
: ?ОБЪЕМ ( длина ширина высота --) \ в любом порядке
3ЗНАЧ 22 > ROT 6 > ROT 19 >
AND AND IF ." Подходит " THEN ;
Автор благодарит за пример Микаэла Хэма.
5.
: РИСУЙ ( n - ) CR 80 100 */ STARS ;
6. а)
0 32 - 10 18 */ . _-17_ok
б)
212 32 - 10 18 */ . _100_ok
в)
-32 32 - 10 18 */ . _-35_ok
г)
16 18 10 */ 32 + . _60_ok
д)
233 273 - . _-40_ok
7.
: Ф>Ц ( фаренг -- цельс) 32 - 10 18 */ ;
: Ц>Ф ( цельс -- фаренг) 18 10 */ 32 + ;
: К>Ц ( кельв -- цельс) 273 - ;
: Ц>К ( цельс -- кельв) 273 + ;
: Ф>К ( фаренг -- кельв) Ф>Ц Ц>К ;
: К>Ф ( кельв -- фаренг) К>Ц Ц>Ф ;
ГЛАВА 6
Упражнения 1-6:
: STARS ( n) 0 ?DO 42 EMIT LOOP ;
: КЛЕТКА ( ширина высота --) 0 DO CR DUP STARS LOOP DROP ;
: \STARS ( #строк --) 0 DO CR I SPACES 10 STARS LOOP ;
: /STARS ( #строк --)
1- 0 SWAP DO CR I SPACES 10 STARS -1 +LOOP ;
\ Определение /STARS с использованием конструкции BEGIN ... UNTIL
: A/STARS ( #строк)
BEGIN 1- CR DUP SPACES 10 STARS DUP 0= UNTIL DROP ;
\ РОМБЫ определены в два этапа
: ТРЕУГОЛЬНИК ( приращение граница индекс --)
DO CR 9 I - SPACES I 2* 1+ STARS DUP +LOOP DROP ;
: РОМБЫ ( #ром6ов --)
0 DO 1 10 0 ТРЕУГОЛЬНИК -1 0 9 ТРЕУГОЛЬНИК LOOP CR ;
7.
: THRU ( от до --) 1+ SWAP DO I DUP . LOAD LOOP ;
8.
: R% ( n1 % -- n2) 10 */ 5 + 10 / ;
: УДВОЕНО ( вклад процент --)
OVER 2* SWAP ROT 21 1 DO
CR ." Год " I 2 .R 3 SPACES
2DUP R% + DUP ." Сумма " ?
DUP 2OVER DROP > IF
CR CR ." Более чем удвоено через " I . ." лет " LEAVE
THEN LOOP 2DROP DROP ;
9.
: ** (n1 n2 -- n1-в-степени-n2)
1 SWAP ?DUP IF 0 DO OVER * LOOP THEN SWAP DROP ;
Спасибо за упражнение Дж.И.Андересну, Эдинбург, Шотландия
ГЛАВА 7
1.
: N-MAX 0 BEGIN 1+ DUP 0< UNTIL 1- . ;
Начиная с нуля, увеличиваем значение на стеке до тех пор, пока оно не станет отрицательным - это означает, что достигнута граница представления целых чисел. Последний оператор 1- возвращает значение перед достижением границы.
2.
: BYNARY 2 BASE ! ;
: БИТОВЫЙ ( #бита -- позиция-бита) 1 SWAP 0 ?DO 2* LOOP ;
: УСТАНОВИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ OR ;
: ОЧИСТИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ -1 XOR AND ;
: ДАЙ-БИТ ( битовый #бита -- бит) БИТОВЫЙ AND ;
: ПЕРЕКЛЮЧИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ XOR ;
: ИЗМЕНЕНИЕ ( (битовый1 битовый2 -- битовый3) XOR ;
3.
: БИП ." Бип " 7 EMIT ;
: ЗАДЕРЖКА 20000 0 DO LOOP ;
: 3ЗВОНКА БИП ЗАДЕРЖКА БИП ЗАДЕРЖКА БИП ;
4. а)
: Ф>Ц -320 М+ 10 18 М*/ ;
: Ц>Ф 18 10 М*/ 320 М+ ;
: К>Ц -2732 М+ ;
: Ц>К 2732 М+ ;
: Ф>К Ф>Ц Ц>К ;
: К>Ф К>Ц Ц>Ф ;
б)
: .ГРАДУС ( d --) DUP >R DABS
<# # 46 HOLD #S R> SIGN #> TYPE SPACE ;
5. В результате получается 17513; считается довольно долго.
: ВЫЧИСЛ ( х -- dv)
DUP 7 М* 20 М+ ROT 1 М*/ 5 M+ ;
: ?DMAX 0 BEGIN 1+ DUP ВЫЧИСЛ 0 0 D< UNTIL 1- . ;
6. В 16-чной системе десятичная цифра имеет такое же значение.
7.
: BINARY 2 BASE ! ;
: 3-СИСТЕМЫ
17 0 DO CR ." Десятичная" DECIMAL I 4 .R 8 SPACES
." 16-ричная" HEX I 3 .R 8 SPACES
." Двоичная" BINARY I S .R 8 SPACES
15 LOOP DECIMAL ;
8. 3.7 интерпретируется как число двойной длины, поскольку содержит десятичную точку, и поэтому занимает два элемента стека. Так как 37 является небольшим числом, то его старшая часть состоит из нулей. "." является оператором над значением одинарной длины; две же точки подряд выводят обе части значения двойной длины. Старшая часть располагается на вершине стека, поэтому ее выводит первая точка. Вторая точка выводит младшую часть - 37.
Число 65536 в точности на единицу превышает число, которое умещается в 16 разрядах. Поэтому 17-й разряд становится равным "1", а все остальные биты превращаются в нули. 17-й бит числа двойной длины является крайним правым битом старшей части. Она выводится как "1". Младшая часть выводится как нули.
Число 65538 больше на два, поэтому младшая часть выглядит как "2".
9. Поскольку данный фрагмент не является словом, форт интерпретирует его как число. Так как NUMBER интерпретирует точку как разделитель целой и дробной части числа, что для него является признаком числа двойной длины, то он на стек поместит ноль двойной длины.
10.
: .ТЕЛЕФОН ( d --) <# # # # # ASCII - HOLD # # #
OVER IF ASCII / HOLD #S THEN #> TYPE SPACE ;
ГЛАВА 8
1. а)
VARIABLE ПИРОЖКИ 0 ПИРОЖКИ !
: ИСПЕКИ-ПИРОЖОК 1 ПИРОЖКИ +! ;
: СЪЕШЬ-ПИРОЖОК ПИРОЖКИ @ IF -1 ПИРОЖКИ +! ." Спасибо "
ELSE ." Какой пирожок?" THEN ;
б)
VARIABLE ЗАМОРОЖЕННЫЕ-ПИРОЖКИ 0 ЗАМОРОЖЕННЫЕ-ПИРОЖКИ !
: ЗАМОРОЗЬ-ПИРОЖКИ ПИРОЖКИ @ ЗАМОРОЖЕННЫЕ-ПИРОЖКИ +! 0 ПИРОЖКИ ! ;
2.
: .БАЗА BASE @ DUP DECIMAL . BASE ! ;
3. Сверх-надежный вариант:
: S>D ( n -- d) DUP 0< ; \ из одинарной в двойную длину
: М. ( d --) TUCK DABS
<# DPL @ DUP -1 <> IF 0 ?DO # LOOP ASCII . HOLD ELSE
DROP S>D THEN #S ROT SIGN #> TYPE SPACE ;
4.
CREATE #КАРАНДАШЕЙ 8 ALLOT \ карандаши четырех цветов
0 CONSTANT КРАСНЫХ 2 CONSTANT ГОЛУБЫХ
4 CONSTANT ЗЕЛЕНЫХ 6 CONSTANT ОРАНЖЕВЫХ
: КАРАНДАШЕЙ ( смещение -- а) #КАРАНДАШЕЙ + ;
23 КРАСНЫХ КАРАНДАШЕЙ !
15 ГОЛУБЫХ КАРАНДАШЕЙ !
12 ЗЕЛЕНЫХ КАРАНДАШЕЙ !
0 ОРАНЖЕВЫХ КАРАНДАШЕЙ !
Для проверки мы можем ввести, например, следующий текст:
ГОЛУБЫХ КАРАНДАШЕЙ ? _15_ok
5.
CREATE 'ШАБЛОНЫ 20 ALLOT ( 10 ячеек)
: ШАБЛОНЫ ( i -- а ) 2* 'ШАБЛОНЫ + ;
: STARS ?DUP IF 0 DO 42 EMIT LOOP THEN ;
: ИНИЦ-ШАБЛОНОВ 10 0 DO 16 MOD I ШАБЛОНЫ ! LOOP ;
: РИСУЙ ( --)
100 DO CR I 2 .R SPACE I ШАБЛОНЫ @ STARS LOOP CR ;
ИНИЦ-ШАБЛОНОВ
6.
1 CONSTANT ЖЕНЩИНА 0 CONSTANT МУЖЧИНА
2 CONSTANT СЕМЕЙНЫЙ 0 CONSTANT ОДИНОКИЙ
4 CONSTANT РАБОТАЕТ 0 CONSTANT HE-РАБОТАЕТ
8 CONSTANT ГОРОДСКОЙ 0 CONSTANT HE-ГОРОДСКОЙ
VARIABLE ВАСЯ
VARIABLE ИРА
: ОПИСАНИЯ ( состояние состояние состояние состояние имярек --)
>R OR OR OR R> ! ;
МУЖЧИНА СЕМЕЙНЫЙ HE-РАБОТАЕТ HE-ГОРОДСКОЙ ВАСЯ ОПИСАНИЯ
ЖЕНЩИНА ОДИНОКИЙ РАБОТАЕТ ГОРОДСКОЙ ИРА ОПИСАНИЯ
: .ПОЛ ( битовый --) ЖЕНЩИНА AND IF ." ЖЕН" THEN ." МУЖ " ;
: .СЕМ-ПОЛ ( битовый --)
СЕМЕЙНЫЙ AND IF ." СЕМЕЙНЫЙ " ELSE ." ОДИНОКИЙ " THEN ;
: .РАБОТА ( битовый --)
РАБОТАЕТ AND 0= IF ." НЕ " THEN ." РАБОТАЕТ " ;
: .ЖИТЕЛЬСТВО ( битовый --)
ГОРОДСКОЙ AND 0= IF ." НЕ " THEN ." ГОРОДСКОЙ " ;
: СВЕДЕНИЯ ( имярек --)
@ DUP .ПОЛ DUP .СЕМ-ПОЛ DUP .РАБОТА .ЖИТЕЛЬСТВО ;
7.
CREATE ПОЛЕ 9 ALLOT
: КВАДРАТ ( #квадрата -- а) ПОЛЕ + ;
: ОЧИСТИТЬ ПОЛЕ 9 0 FILL ; ОЧИСТИТЬ
: ЛИНИЯ ." : " ;
: ПОДЧЕРКИВАНИЕ CR 9 0 DO ASCII - EMIT LOOP CR ;
: .КЛЕТКА ( #квадрата --) КВАДРАТ С@ DUP 0= IF 2 SPACES ELSE
DUP 1 = IF ." X " ELSE ." 0 " THEN THEN DROP ;
: КАРТИНКА CR 9 0 DO I IF I 3 MOD 0= IF
ПОДЧЕРКИВАНИЕ ELSE ЛИНИЯ THEN THEN I .КЛЕТКА LOOP CR QUIT ;
: ХОД ( игрок #квадрата --)
1- 0 MAX 8 MIN КВАДРАТ С! ;
: X! ( #квадрата --) 1 SWAP ХОД КАРТИНКА ;
: 0! ( #квадрата --) -1 SWAP ХОД КАРТИНКА ;
ГЛАВА 9
1.
VARIABLE 'ПОЛУЧАЕМ
: ПОЛУЧАЕМ ( n n - ) 'ПОЛУЧАЕМ @ EXECUTE . ;
: СКЛАДЫВАЯ ['] + 'ПОЛУЧАЕМ ! ;
: УМНОЖАЯ ['] * 'ПОЛУЧАЕМ ! ;
2. Вы можете узнать это, введя
HERE U.
в начале работы или после применения системных команд, очищающих словарь, таких как COLD или EMPTY.
3. Вы можете узнать это, введя
PAD HERE - U.
4. а) Разницы нет. Переменная оставляет на стеке собственный pfa.
б) Пользовательская переменная оставляет на стеке адрес ячейки из пользовательской таблицы. Элемент словаря, который ищется словом, может находиться где угодно.
5. Решение 1:
CREATE 'ЧТО-ДЕЛАТЬ 12 ALLOT \ 6 ячеек
: ЧТО-ДЕЛАТЬ ( i -- а) 0 МАХ 5 MIN 2* 'ЧТО-ДЕЛАТЬ + ;
: ВСТРЕЧА ." Привет, я говорю на форте. " ;
: ПОСЛЕДОВАТЕЛЬНОСТЬ 11 1 DO I . LOOP ;
: ПЛИТКА 10 5 КЛЕТКА ; \ См. ответы к главе 6
: НИЧЕГО ;
' ВСТРЕЧА 0 ЧТО-ДЕЛАТЬ ! ' ПОСЛЕДОВАТЕЛЬНОСТЬ 1 ЧТО-ДЕЛАТЬ !
' ПЛИТКА 2 ЧТО-ДЕЛАТЬ ! ' НИЧЕГО 3 ЧТО-ДЕЛАТЬ !
' НИЧЕГО 4 ЧТО-ДЕЛАТЬ ! ' НИЧЕГО 5 ЧТО-ДЕЛАТЬ !
: ЧТО-НИБУДЬ ( индекс --) ЧТО-ДЕЛАТЬ @EXECUTE ;
Решение 2:
CREATE 'ЧТО-ДЕЛАТЬ 12 ALLOT \ 6 ячеек
: ЧТО-ДЕЛАТЬ ( i -- а) 0 MАХ 5 MIN 2* 'ЧТО-ДЕЛАТЬ + ;
: ВСТРЕЧА ." Привет, я говорю на форте. " ;
: ПОСЛЕДОВАТЕЛЬНОСТЬ 11 1 DO I . LOOP ;
: ПЛИТКА 13 5 КЛЕТКА 5 \ см. ответы к главе 6
: НИЧЕГО ;
: ИНИЦИАЛИЗАЦИЯ ( --)
6 0 DO ['] НИЧЕГО I ЧТО-ДЕЛАТЬ ! LOOP
['] ВСТРЕЧА 0 ЧТО-ДЕЛАТЬ ! ['] ПОСЛЕДОВАТЕЛЬНОСТЬ 1 ЧТО-ДЕЛАТЬ !
['] ПЛИТКА 2 ЧТО-ДЕЛАТЬ ! ;
ИНИЦИАЛИЗАЦИЯ
: ЧТО-НИБУДЬ ( индекс --) ЧТО-ДЕЛАТЬ @EXECUTE ;
ГЛАВА 10
1.
: СИМВОЛ ( i -- а)
228 BLOCK + ;
: ЗАМЕНА ( c1 c2 --) \ замена c1 на c2
1024 0 DO OVER I СИМВОЛ С@ = IF DUP I СИМВОЛ С!
UPDATE THEN LOOP 2DROP ;
2.
181 LOAD \ Случайные числа
\ ??? CONSTANT ПРЕДСКАЗАНИЯ \ номер блока под сообщения
: ПРЕДСКАЗАНИЕ CR 16 CHOOSE 64 * ПРЕДСКАЗАНИЯ BLOCK +
64 -TRAILING TYPE SPACE ;
Вы можете обращаться к своим собственным "предсказаниям". Занесите их по одному на строку в свободный блок, а затем занесите номер этого блока в приведенное выше определение константы ПРЕДСКАЗАНИЯ.
3. а)
: ДА/НЕТ? ( -- t=Y | f=прочее) KEY DUP EMIT ASCII Y = ;
б)
: ДА/НЕТ? ( -- t=Y | f=прочее)
KEY 95 AND DUP EMIT ASCII Y = ;
в) два возможных решения:
: ДА/НЕТ? ( -- t=Y | f=N )
BEGIN KEY 95 AND DUP ASCII Y = IF DROP TRUE EXIT ELSE
DUP ASCII N = IF 0= EXIT THEN THEN DROP FALSE UNTIL ;
: ДА/НЕТ? ( -- t=Y | f=N )
BEGIN KEY 95 AND DUP ASCII Y = OVER ASCII N = OR NOT
WHILE DROP REPEAT ASCII Y = ;
4.
: ЖИВОТНЫЕ LIT" КРЫСЫ БЫКА ТИГРА КРОЛИКА ДРАКОНА ЗМЕИ ЛОШАДИ
БАРАНА ОБЕЗЬЯНЫ ПЕТУХА СОБАКИ СВИНЬИ " ;
: .ЖИВОТНОЕ ( u --) \ и изменяется от 0 до 11
8 * ЖИВОТНЫЕ 1+ + 8 -TRAILING TYPE ;
: (ГОРОСКОП) ( год --)
1900 - 12 MOD
." Вы родились в год " .ЖИВОТНОЕ
ASCII . EMIT CR ;
350 351 THRU \ загрузка определения EXPECT#
: ЦИФРЫ ( #цифр -- d )
DUP 0 DO ASCII EMIT LOOP DUP 0 DO ЗАБОЙ LOOP
PAD SWAP 2DUP 1+ BLANK EXPECT# DROP PAD 1- NUMBER ;
: ГОРОСКОП
CR ." В каком году вы родились? " 4 ЦИФРЫ
CR DROP (ГОРОСКОП) ;
5.
VARIABLE СТРОКА
: начало 0 строка ' ;
: добавить \ 1прилагательное, 2прилагательное, 3прилагательное,
\ существительное ( --)
СТРОКА @ 0 БРЕД 60 BLANK UPDATE
3 0 DO
ASCII , WORD COUNT СТРОКА @ I БРЕД SWAP CMOVE UPDATE
LOOP 1 СТРОКА +! ;
или, используя TEXT:
: добавить \ 1прилагательное, 2прилагательное, 3прилагательное,
\ существительное ( - )
3 0 DO
ASCII , TEXT PAD СТРОКА @ I БРЕД 28 CMOVE UPDATE
LOOP 1 СТРОКА +! ;
6.
: >ДАТА ( а -- n n )
0 0 ROT CONVERT ROT >R 0 SWAP CONVERT ROT >R
0 SWAP CONVERT 2DROP 1900 + R> R> 256 * + SWAP ;
: СКАНИРОВАНИЕ BL WORD >ДАТА ;
7.
8 VARIABLE STUFF \ первым блоком файла
300 STUFF ! \ является блок 300
: ЭЛЕМЕНТ ( i -- а)
2* 1024 /MOD STUFF @ + BLOCK + UPDATE ;
\ Проверка виртуального массива:
: ИНИЦИАЛ-МАССИВ 600 0 DO I I ЭЛЕМЕНТ ! LOOP ;
: .МАССИВ 600 0 DO I . SPACE I ЭЛЕМЕНТ ? LOOP ;
\ Теперь преобразуем виртуальный массив в файл:
: ИСПОЛЬЗОВАНО ( -- а) СВОБ @ BLOCK UPDATE ;
\ Переопределим ЭЛЕМЕНТ так, чтобы ИСПОЛЬЗОВАННЫЕ пропускались:
: ЭЛЕМЕНТ ( i -- а)
1+ 2* 1024 /MOD СВОБ @ + BLOCK + UPDATE ;
: НЕТ-ИСП 0 ИСПОЛЬЗОВАНО ! ;
НЕТ-ИСП
: ПОМЕСТИТЬ ( n --) ИСПОЛЬЗОВАНО @ ЭЛЕМЕНТ ! 1 ИСПОЛЬЗОВАНО +! ;
: ВНЕСТИ ( n1 n2 --) SWAP ПОМЕСТИТЬ ПОМЕСТИТЬ ;
: ТАБЛИЦА CR ИСПОЛЬЗОВАНО @ 0 ?DO I 8 МOD 0= IF CR THEN
I ЭЛЕМЕНТ @ 8 .R LOOP CR ;
ГЛАВА 11
1.
: ЗАГРУЗКА ( n --) CREATE , DOES> ( --) @ LOAD ;
2.
: ОСНОВАНИЕ. ( n --) CREATE ,
DOES> ( n --) @ BASE @ SWAP BASE ' SWAP . BASE ! ;
3.
: МНОГО ( a --) CREATE ,
DOES> ( --) @ SWAP 0 ?DO DUP EXECUTE LOOP DROP ;
' CR МНОГО CRS
4 CRS
4.
: TURNE [COMPILE] DO ; IMMEDIATE
: RETURNE [COMPILE] LOOP ; IMMEDIATE
: ПОПЫТКА 10 0 TURNE I . RETURNE ;
5.
: ЦИКЛЫ ( #раз --)
>IN @ SWAP 0 DO DUP >IN ! INTERPRET LOOP DROP ;
6.
: STAR * 42 EMIT ;
: .РЯД ( b --) \ вывод звездочки на каждый бит из байта
CR 8 0 DO DUP 128 AND IF STAR ELSE SPACE THEN
2* LOOP DROP ;
VARIABLE ШАБЛОН
: БИТ ( t=не-пробел --)
1 AND ШАБЛОН @ 2* + ШАБЛОН ! ;
: ЗВЕЗДЫ>БИТЫ ( а -- b)
0 ШАБЛОН ! 8 OVER + SWAP DO I С@ BL <> БИТ LOOP
ШАБЛОН @ ;
: ЧТ-РЯД ( -- b)
ASCII ! WORD COUNT + 8 - ЗВЕЗДЫ>БИТЫ ;
: ФОРМА CREATE 8 0 DO ЧТ-РЯД С, . LOOP
DOES> 8 OVER + SWAP DO I С@ .РЯД LOOP CR ;
ФОРМА .L XXX_____!
_X______!
_X______!
_X______!
_X______!
_X______!
_X_____X!
XXXXXXXX!
ФОРМА .В ХХХХХХХ_!
_X_____X!
_X_____X!
_XXXXXX_!
_X_____X!
_X_____X!
_X_____X!
XXXXXXX_!
ГЛАВА 12
1.
: ширина ( смещение длина -- нов-смещение )
CREATE OVER , DUP , + ;
13 \ начальная позиция внутри записи
16 ширина фамилия
12 ширина имя
24 ширина работа
12 ширина телефон
CONSTANT /ЗАПИСЬ ( количество байт на запись ) EXIT
По приведенным выше правилам компилируются одинаковые структуры. "ширина" является определяющим словом, которое хранит на стеке текущую позицию внутри поля. Для каждого паля "ширина" компилирует смещение и длину, а затем их складывает, чтобы получить следующее смещение. После того, как определится "телефон", полученное а результате значение является длиной всей записи и превращается в константу /ЗАПИСЬ .
2.
: вызвать ( имя ( --)
имя запомнить ВВЕРХ -НАЙТИ IF ОТСУТСТВУЕТ
ELSE CR .ИМЯ телефон .ПОЛЕ THEN ;
ГЛАВА 1
1.
: ДАР ." подставку для книг" ;
: ДАРИТЕЛЬ ." Маша" ;
: БЛАГОДАРНОСТЬ CR ." Дорогая " ДАРИТЕЛЬ ." ," CR
5 SPACES ." спасибо за " ДАР ." . " ;
2.
: МЕНЬШЕ-НА-ДЕСЯТЬ ( n -- n-10 ) -10 + ;
или
: МЕНЬШЕ-НА-ДЕСЯТЬ ( n -- n-10 ) 10 - ;
3. При компиляции определения БЛАГОДАРНОСТЬ компилятор включает в него определение слова ДАРИТЕЛЬ, существующее на момент компиляции. Если вы после компиляции слова БЛАГОДАРНОСТЬ добавите в словарь новый вариант слова ДАРИТЕЛЬ, то этот никак не отразится на уже скомпилированном слове БЛАГОДАРНОСТЬ. (Но вы можете переопределить (перекомпилировать) и слово БЛАГОДАРНОСТЬ. В этом случае в его определение войдет новый вариант слова ДАРИТЕЛЬ).
ГЛАВА 2
ОТВЕТЫ К УПРАЖНЕНИЮ 2-A
1. a b + c * или c a b + *
2. a b * 100 /
3. 3 a * b - 4 / c +
4. a 1 + 4 /
5. 7 x * 5 + x *
6. (a - b) / (b + a)
7. a / 10b
ОТВЕТЫ К УПРАЖНЕНИЮ 2-Б
2.
: 2Б2 ( c a b -- x) 4 * - 6 / + ;
3.
: 2Б3 ( a b -- x) 8 * / ;
4.
: 2Б4 ( a b -- x) * 200 / ;
5.
: 2Б5 ( a a -- x) 2 * 3 + * ;
6. Если вы скажете, что такое выражение преобразовать нельзя, то будете правы, по крайней мере сейчас, пока мы еще не рассмотрели специальных стековых операций.
ОТВЕТЫ К УПРАЖНЕНИЮ 2-B
1. SWAP ROT
2. SWAP DUP ROT SWAP
3.
: 2B3 DUP 1 + SWAP / ;
или
: 2B3 DUP 1+ SWAP / ;
4.
: 2B4 DUP 7 * 5 + * ;
5.
: 2B5 OVER 9 * SWAP - * ;
В КОНЦЕ ГЛАВЫ 2
1.
DUP DUP (1 2 -- 1 2 2 2)
2DUP (1 2 -- 1 2 1 2)
2.
: NIP ( а b -- b) SWAP DROP ;
3.
: TUCK ( а b - Ь a b) SWAP OVER ;
4.
: -ROT ( а b с - cab) ROT ROT ;
5. SWAP 2SWAP SWAP
6.
: 3DUP ( n1 n2 n3 -- n1 n2 n3 n1 n2 n3) DUP 2OVER ROT ;
7.
: 2-7 ( c a b -- n) OVER + * + ;
8.
: 2-8 ( a b -- n) 2DUP - ROT ROT + / ;
9.
: УПАКОВКА ( #яиц --)
12 /MOD . ." коробок и " . ." не упаковано " ;
ГЛАВА 4
1.
-1 0= NOT . _-1_ok
0 0= NOT . _0_ok
200 0= NOT . _-1_ok
2. Не спросит ничего.
3. Употреблять спиртные напитки можно только с 21 года, тогда:
: РАЗРЕШЕНИЕ ( возраст --)
20 > IF ." Употребление алкоголя разрешено "
ELSE ." Вы еще молоды " THEN ;
4.
: ЗНАКИ ( n) DUP 0= IF ." Нуль " ELSE
DUP 0< IF ." Отрицательное " ELSE
." Положительное " THEN THEN DROP ;
Или как-нибудь иначе - лишь бы работало.
5.
: <> ( n1 n2 -- ?) = NOT ;
6.
: XOR ( x y -- ?)
2DUP NOT AND SWAP ROT NOT AND OR ;
7.
: STARS ( n --) ?DUP IF STARS THEN ;
8.
: NEGATE ( n -- -n) 0 SWAP - ;
: ABS ( n - |n| ) DUP 0< IF NEGATE THEN ;
9.
: /UP ( делимое делитель -- частное) /MOD SWAP IF 1+ THEN ;
10.
: -ROT ( a b c -- c a b) ROT ROT ;
: WITHIN ( n l h -- ?) -ROT OVER > NOT -ROT > AND ;
Ниже приводится более эффективный вариант, в котором используются приемы, рассмотренные в следующих главах:
: WITHIN ( n l h -- ?) OVER - >R - R> U< ;
11.
: УГАДАЙ ( ответ попытка -- ответ )
2DUP = IF ." Вы угадали! " 2DROP ELSE
2DUP < IF ." Слишком много " ELSE
." Слишком мало " THEN DROP THEN ;
12.
: .ОТРИЦАТЕЛЬНОЕ ( n -- |n|) 0< IF ." Отрицательное " ABS THEN ;
: ПРОПИСЬ ( n --) DUP ABS 4 > IF ." Выходит за границу " ELSE
DUP .ОТРИЦАТЕЛЬНОЕ DUP 0= IF ." Нуль " ELSE
DUP 1 = IF ." Один " ELSE
DUP 2 = IF ." Два " ELSE
DUP 3 = IF ." Три " ELSE
." Четыре "
THEN THEN THEN THEN THEN DROP ;
13. В предположении, что -ROT и WITHIN уже загружены:
: 3DUP ( a b c -- a b c a b c) DUP 2OVER ROT ;
: ЛОВУШКА ( ответ -меньш-число -большее-число -- ответ | --)
3DUP OVER = -ROT = AND
IF ."Вы угадали! " 2DROP DROP ELSE
3DUP SWAP 1 + SWAP WITHIN IF ." Между "
ELSE ." Вне " THEN 2DROP THEN ;
ГЛАВА 5
1. -1 интерпретируется как число "отрицательная единица"; 1- является словом форта, которое вычитает единицу из значения на стеке.
2. */ NEGATE
3. МАХ МАХ МАХ .
4. а)
: 2ЗНАЧ ( n1 n2 -- n? n?) \ помещение большего значения на вершину
2DUP > IF SWAP THEN ;
б)
: 3ЗНАЧ ( n1 n2 n3 -- n? n? n?) \ большее значение на вершину
2ЗНАЧ >R 2ЗНАЧ R> 2ЗНАЧ ;
Вы можете продолжать в том же духе и далее...
: 4ЗНАЧ \ иэ четырех элементов стека больший поместить в вершину
3ЗНАЧ >R 3ЗНАЧ R> 2ЗНАЧ ;
... последним оператором во всем случаях должен быть 2ЗНАЧ .
в)
: ?ОБЪЕМ ( длина ширина высота --) \ в любом порядке
3ЗНАЧ 22 > ROT 6 > ROT 19 >
AND AND IF ." Подходит " THEN ;
Автор благодарит за пример Микаэла Хэма.
5.
: РИСУЙ ( n - ) CR 80 100 */ STARS ;
6. а)
0 32 - 10 18 */ . _-17_ok
б)
212 32 - 10 18 */ . _100_ok
в)
-32 32 - 10 18 */ . _-35_ok
г)
16 18 10 */ 32 + . _60_ok
д)
233 273 - . _-40_ok
7.
: Ф>Ц ( фаренг -- цельс) 32 - 10 18 */ ;
: Ц>Ф ( цельс -- фаренг) 18 10 */ 32 + ;
: К>Ц ( кельв -- цельс) 273 - ;
: Ц>К ( цельс -- кельв) 273 + ;
: Ф>К ( фаренг -- кельв) Ф>Ц Ц>К ;
: К>Ф ( кельв -- фаренг) К>Ц Ц>Ф ;
ГЛАВА 6
Упражнения 1-6:
: STARS ( n) 0 ?DO 42 EMIT LOOP ;
: КЛЕТКА ( ширина высота --) 0 DO CR DUP STARS LOOP DROP ;
: \STARS ( #строк --) 0 DO CR I SPACES 10 STARS LOOP ;
: /STARS ( #строк --)
1- 0 SWAP DO CR I SPACES 10 STARS -1 +LOOP ;
\ Определение /STARS с использованием конструкции BEGIN ... UNTIL
: A/STARS ( #строк)
BEGIN 1- CR DUP SPACES 10 STARS DUP 0= UNTIL DROP ;
\ РОМБЫ определены в два этапа
: ТРЕУГОЛЬНИК ( приращение граница индекс --)
DO CR 9 I - SPACES I 2* 1+ STARS DUP +LOOP DROP ;
: РОМБЫ ( #ром6ов --)
0 DO 1 10 0 ТРЕУГОЛЬНИК -1 0 9 ТРЕУГОЛЬНИК LOOP CR ;
7.
: THRU ( от до --) 1+ SWAP DO I DUP . LOAD LOOP ;
8.
: R% ( n1 % -- n2) 10 */ 5 + 10 / ;
: УДВОЕНО ( вклад процент --)
OVER 2* SWAP ROT 21 1 DO
CR ." Год " I 2 .R 3 SPACES
2DUP R% + DUP ." Сумма " ?
DUP 2OVER DROP > IF
CR CR ." Более чем удвоено через " I . ." лет " LEAVE
THEN LOOP 2DROP DROP ;
9.
: ** (n1 n2 -- n1-в-степени-n2)
1 SWAP ?DUP IF 0 DO OVER * LOOP THEN SWAP DROP ;
Спасибо за упражнение Дж.И.Андересну, Эдинбург, Шотландия
ГЛАВА 7
1.
: N-MAX 0 BEGIN 1+ DUP 0< UNTIL 1- . ;
Начиная с нуля, увеличиваем значение на стеке до тех пор, пока оно не станет отрицательным - это означает, что достигнута граница представления целых чисел. Последний оператор 1- возвращает значение перед достижением границы.
2.
: BYNARY 2 BASE ! ;
: БИТОВЫЙ ( #бита -- позиция-бита) 1 SWAP 0 ?DO 2* LOOP ;
: УСТАНОВИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ OR ;
: ОЧИСТИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ -1 XOR AND ;
: ДАЙ-БИТ ( битовый #бита -- бит) БИТОВЫЙ AND ;
: ПЕРЕКЛЮЧИТЬ-БИТ ( битовый1 #бита -- битовый2) БИТОВЫЙ XOR ;
: ИЗМЕНЕНИЕ ( (битовый1 битовый2 -- битовый3) XOR ;
3.
: БИП ." Бип " 7 EMIT ;
: ЗАДЕРЖКА 20000 0 DO LOOP ;
: 3ЗВОНКА БИП ЗАДЕРЖКА БИП ЗАДЕРЖКА БИП ;
4. а)
: Ф>Ц -320 М+ 10 18 М*/ ;
: Ц>Ф 18 10 М*/ 320 М+ ;
: К>Ц -2732 М+ ;
: Ц>К 2732 М+ ;
: Ф>К Ф>Ц Ц>К ;
: К>Ф К>Ц Ц>Ф ;
б)
: .ГРАДУС ( d --) DUP >R DABS
<# # 46 HOLD #S R> SIGN #> TYPE SPACE ;
5. В результате получается 17513; считается довольно долго.
: ВЫЧИСЛ ( х -- dv)
DUP 7 М* 20 М+ ROT 1 М*/ 5 M+ ;
: ?DMAX 0 BEGIN 1+ DUP ВЫЧИСЛ 0 0 D< UNTIL 1- . ;
6. В 16-чной системе десятичная цифра имеет такое же значение.
7.
: BINARY 2 BASE ! ;
: 3-СИСТЕМЫ
17 0 DO CR ." Десятичная" DECIMAL I 4 .R 8 SPACES
." 16-ричная" HEX I 3 .R 8 SPACES
." Двоичная" BINARY I S .R 8 SPACES
15 LOOP DECIMAL ;
8. 3.7 интерпретируется как число двойной длины, поскольку содержит десятичную точку, и поэтому занимает два элемента стека. Так как 37 является небольшим числом, то его старшая часть состоит из нулей. "." является оператором над значением одинарной длины; две же точки подряд выводят обе части значения двойной длины. Старшая часть располагается на вершине стека, поэтому ее выводит первая точка. Вторая точка выводит младшую часть - 37.
Число 65536 в точности на единицу превышает число, которое умещается в 16 разрядах. Поэтому 17-й разряд становится равным "1", а все остальные биты превращаются в нули. 17-й бит числа двойной длины является крайним правым битом старшей части. Она выводится как "1". Младшая часть выводится как нули.
Число 65538 больше на два, поэтому младшая часть выглядит как "2".
9. Поскольку данный фрагмент не является словом, форт интерпретирует его как число. Так как NUMBER интерпретирует точку как разделитель целой и дробной части числа, что для него является признаком числа двойной длины, то он на стек поместит ноль двойной длины.
10.
: .ТЕЛЕФОН ( d --) <# # # # # ASCII - HOLD # # #
OVER IF ASCII / HOLD #S THEN #> TYPE SPACE ;
ГЛАВА 8
1. а)
VARIABLE ПИРОЖКИ 0 ПИРОЖКИ !
: ИСПЕКИ-ПИРОЖОК 1 ПИРОЖКИ +! ;
: СЪЕШЬ-ПИРОЖОК ПИРОЖКИ @ IF -1 ПИРОЖКИ +! ." Спасибо "
ELSE ." Какой пирожок?" THEN ;
б)
VARIABLE ЗАМОРОЖЕННЫЕ-ПИРОЖКИ 0 ЗАМОРОЖЕННЫЕ-ПИРОЖКИ !
: ЗАМОРОЗЬ-ПИРОЖКИ ПИРОЖКИ @ ЗАМОРОЖЕННЫЕ-ПИРОЖКИ +! 0 ПИРОЖКИ ! ;
2.
: .БАЗА BASE @ DUP DECIMAL . BASE ! ;
3. Сверх-надежный вариант:
: S>D ( n -- d) DUP 0< ; \ из одинарной в двойную длину
: М. ( d --) TUCK DABS
<# DPL @ DUP -1 <> IF 0 ?DO # LOOP ASCII . HOLD ELSE
DROP S>D THEN #S ROT SIGN #> TYPE SPACE ;
4.
CREATE #КАРАНДАШЕЙ 8 ALLOT \ карандаши четырех цветов
0 CONSTANT КРАСНЫХ 2 CONSTANT ГОЛУБЫХ
4 CONSTANT ЗЕЛЕНЫХ 6 CONSTANT ОРАНЖЕВЫХ
: КАРАНДАШЕЙ ( смещение -- а) #КАРАНДАШЕЙ + ;
23 КРАСНЫХ КАРАНДАШЕЙ !
15 ГОЛУБЫХ КАРАНДАШЕЙ !
12 ЗЕЛЕНЫХ КАРАНДАШЕЙ !
0 ОРАНЖЕВЫХ КАРАНДАШЕЙ !
Для проверки мы можем ввести, например, следующий текст:
ГОЛУБЫХ КАРАНДАШЕЙ ? _15_ok
5.
CREATE 'ШАБЛОНЫ 20 ALLOT ( 10 ячеек)
: ШАБЛОНЫ ( i -- а ) 2* 'ШАБЛОНЫ + ;
: STARS ?DUP IF 0 DO 42 EMIT LOOP THEN ;
: ИНИЦ-ШАБЛОНОВ 10 0 DO 16 MOD I ШАБЛОНЫ ! LOOP ;
: РИСУЙ ( --)
100 DO CR I 2 .R SPACE I ШАБЛОНЫ @ STARS LOOP CR ;
ИНИЦ-ШАБЛОНОВ
6.
1 CONSTANT ЖЕНЩИНА 0 CONSTANT МУЖЧИНА
2 CONSTANT СЕМЕЙНЫЙ 0 CONSTANT ОДИНОКИЙ
4 CONSTANT РАБОТАЕТ 0 CONSTANT HE-РАБОТАЕТ
8 CONSTANT ГОРОДСКОЙ 0 CONSTANT HE-ГОРОДСКОЙ
VARIABLE ВАСЯ
VARIABLE ИРА
: ОПИСАНИЯ ( состояние состояние состояние состояние имярек --)
>R OR OR OR R> ! ;
МУЖЧИНА СЕМЕЙНЫЙ HE-РАБОТАЕТ HE-ГОРОДСКОЙ ВАСЯ ОПИСАНИЯ
ЖЕНЩИНА ОДИНОКИЙ РАБОТАЕТ ГОРОДСКОЙ ИРА ОПИСАНИЯ
: .ПОЛ ( битовый --) ЖЕНЩИНА AND IF ." ЖЕН" THEN ." МУЖ " ;
: .СЕМ-ПОЛ ( битовый --)
СЕМЕЙНЫЙ AND IF ." СЕМЕЙНЫЙ " ELSE ." ОДИНОКИЙ " THEN ;
: .РАБОТА ( битовый --)
РАБОТАЕТ AND 0= IF ." НЕ " THEN ." РАБОТАЕТ " ;
: .ЖИТЕЛЬСТВО ( битовый --)
ГОРОДСКОЙ AND 0= IF ." НЕ " THEN ." ГОРОДСКОЙ " ;
: СВЕДЕНИЯ ( имярек --)
@ DUP .ПОЛ DUP .СЕМ-ПОЛ DUP .РАБОТА .ЖИТЕЛЬСТВО ;
7.
CREATE ПОЛЕ 9 ALLOT
: КВАДРАТ ( #квадрата -- а) ПОЛЕ + ;
: ОЧИСТИТЬ ПОЛЕ 9 0 FILL ; ОЧИСТИТЬ
: ЛИНИЯ ." : " ;
: ПОДЧЕРКИВАНИЕ CR 9 0 DO ASCII - EMIT LOOP CR ;
: .КЛЕТКА ( #квадрата --) КВАДРАТ С@ DUP 0= IF 2 SPACES ELSE
DUP 1 = IF ." X " ELSE ." 0 " THEN THEN DROP ;
: КАРТИНКА CR 9 0 DO I IF I 3 MOD 0= IF
ПОДЧЕРКИВАНИЕ ELSE ЛИНИЯ THEN THEN I .КЛЕТКА LOOP CR QUIT ;
: ХОД ( игрок #квадрата --)
1- 0 MAX 8 MIN КВАДРАТ С! ;
: X! ( #квадрата --) 1 SWAP ХОД КАРТИНКА ;
: 0! ( #квадрата --) -1 SWAP ХОД КАРТИНКА ;
ГЛАВА 9
1.
VARIABLE 'ПОЛУЧАЕМ
: ПОЛУЧАЕМ ( n n - ) 'ПОЛУЧАЕМ @ EXECUTE . ;
: СКЛАДЫВАЯ ['] + 'ПОЛУЧАЕМ ! ;
: УМНОЖАЯ ['] * 'ПОЛУЧАЕМ ! ;
2. Вы можете узнать это, введя
HERE U.
в начале работы или после применения системных команд, очищающих словарь, таких как COLD или EMPTY.
3. Вы можете узнать это, введя
PAD HERE - U.
4. а) Разницы нет. Переменная оставляет на стеке собственный pfa.
б) Пользовательская переменная оставляет на стеке адрес ячейки из пользовательской таблицы. Элемент словаря, который ищется словом, может находиться где угодно.
5. Решение 1:
CREATE 'ЧТО-ДЕЛАТЬ 12 ALLOT \ 6 ячеек
: ЧТО-ДЕЛАТЬ ( i -- а) 0 МАХ 5 MIN 2* 'ЧТО-ДЕЛАТЬ + ;
: ВСТРЕЧА ." Привет, я говорю на форте. " ;
: ПОСЛЕДОВАТЕЛЬНОСТЬ 11 1 DO I . LOOP ;
: ПЛИТКА 10 5 КЛЕТКА ; \ См. ответы к главе 6
: НИЧЕГО ;
' ВСТРЕЧА 0 ЧТО-ДЕЛАТЬ ! ' ПОСЛЕДОВАТЕЛЬНОСТЬ 1 ЧТО-ДЕЛАТЬ !
' ПЛИТКА 2 ЧТО-ДЕЛАТЬ ! ' НИЧЕГО 3 ЧТО-ДЕЛАТЬ !
' НИЧЕГО 4 ЧТО-ДЕЛАТЬ ! ' НИЧЕГО 5 ЧТО-ДЕЛАТЬ !
: ЧТО-НИБУДЬ ( индекс --) ЧТО-ДЕЛАТЬ @EXECUTE ;
Решение 2:
CREATE 'ЧТО-ДЕЛАТЬ 12 ALLOT \ 6 ячеек
: ЧТО-ДЕЛАТЬ ( i -- а) 0 MАХ 5 MIN 2* 'ЧТО-ДЕЛАТЬ + ;
: ВСТРЕЧА ." Привет, я говорю на форте. " ;
: ПОСЛЕДОВАТЕЛЬНОСТЬ 11 1 DO I . LOOP ;
: ПЛИТКА 13 5 КЛЕТКА 5 \ см. ответы к главе 6
: НИЧЕГО ;
: ИНИЦИАЛИЗАЦИЯ ( --)
6 0 DO ['] НИЧЕГО I ЧТО-ДЕЛАТЬ ! LOOP
['] ВСТРЕЧА 0 ЧТО-ДЕЛАТЬ ! ['] ПОСЛЕДОВАТЕЛЬНОСТЬ 1 ЧТО-ДЕЛАТЬ !
['] ПЛИТКА 2 ЧТО-ДЕЛАТЬ ! ;
ИНИЦИАЛИЗАЦИЯ
: ЧТО-НИБУДЬ ( индекс --) ЧТО-ДЕЛАТЬ @EXECUTE ;
ГЛАВА 10
1.
: СИМВОЛ ( i -- а)
228 BLOCK + ;
: ЗАМЕНА ( c1 c2 --) \ замена c1 на c2
1024 0 DO OVER I СИМВОЛ С@ = IF DUP I СИМВОЛ С!
UPDATE THEN LOOP 2DROP ;
2.
181 LOAD \ Случайные числа
\ ??? CONSTANT ПРЕДСКАЗАНИЯ \ номер блока под сообщения
: ПРЕДСКАЗАНИЕ CR 16 CHOOSE 64 * ПРЕДСКАЗАНИЯ BLOCK +
64 -TRAILING TYPE SPACE ;
Вы можете обращаться к своим собственным "предсказаниям". Занесите их по одному на строку в свободный блок, а затем занесите номер этого блока в приведенное выше определение константы ПРЕДСКАЗАНИЯ.
3. а)
: ДА/НЕТ? ( -- t=Y | f=прочее) KEY DUP EMIT ASCII Y = ;
б)
: ДА/НЕТ? ( -- t=Y | f=прочее)
KEY 95 AND DUP EMIT ASCII Y = ;
в) два возможных решения:
: ДА/НЕТ? ( -- t=Y | f=N )
BEGIN KEY 95 AND DUP ASCII Y = IF DROP TRUE EXIT ELSE
DUP ASCII N = IF 0= EXIT THEN THEN DROP FALSE UNTIL ;
: ДА/НЕТ? ( -- t=Y | f=N )
BEGIN KEY 95 AND DUP ASCII Y = OVER ASCII N = OR NOT
WHILE DROP REPEAT ASCII Y = ;
4.
: ЖИВОТНЫЕ LIT" КРЫСЫ БЫКА ТИГРА КРОЛИКА ДРАКОНА ЗМЕИ ЛОШАДИ
БАРАНА ОБЕЗЬЯНЫ ПЕТУХА СОБАКИ СВИНЬИ " ;
: .ЖИВОТНОЕ ( u --) \ и изменяется от 0 до 11
8 * ЖИВОТНЫЕ 1+ + 8 -TRAILING TYPE ;
: (ГОРОСКОП) ( год --)
1900 - 12 MOD
." Вы родились в год " .ЖИВОТНОЕ
ASCII . EMIT CR ;
350 351 THRU \ загрузка определения EXPECT#
: ЦИФРЫ ( #цифр -- d )
DUP 0 DO ASCII EMIT LOOP DUP 0 DO ЗАБОЙ LOOP
PAD SWAP 2DUP 1+ BLANK EXPECT# DROP PAD 1- NUMBER ;
: ГОРОСКОП
CR ." В каком году вы родились? " 4 ЦИФРЫ
CR DROP (ГОРОСКОП) ;
5.
VARIABLE СТРОКА
: начало 0 строка ' ;
: добавить \ 1прилагательное, 2прилагательное, 3прилагательное,
\ существительное ( --)
СТРОКА @ 0 БРЕД 60 BLANK UPDATE
3 0 DO
ASCII , WORD COUNT СТРОКА @ I БРЕД SWAP CMOVE UPDATE
LOOP 1 СТРОКА +! ;
или, используя TEXT:
: добавить \ 1прилагательное, 2прилагательное, 3прилагательное,
\ существительное ( - )
3 0 DO
ASCII , TEXT PAD СТРОКА @ I БРЕД 28 CMOVE UPDATE
LOOP 1 СТРОКА +! ;
6.
: >ДАТА ( а -- n n )
0 0 ROT CONVERT ROT >R 0 SWAP CONVERT ROT >R
0 SWAP CONVERT 2DROP 1900 + R> R> 256 * + SWAP ;
: СКАНИРОВАНИЕ BL WORD >ДАТА ;
7.
8 VARIABLE STUFF \ первым блоком файла
300 STUFF ! \ является блок 300
: ЭЛЕМЕНТ ( i -- а)
2* 1024 /MOD STUFF @ + BLOCK + UPDATE ;
\ Проверка виртуального массива:
: ИНИЦИАЛ-МАССИВ 600 0 DO I I ЭЛЕМЕНТ ! LOOP ;
: .МАССИВ 600 0 DO I . SPACE I ЭЛЕМЕНТ ? LOOP ;
\ Теперь преобразуем виртуальный массив в файл:
: ИСПОЛЬЗОВАНО ( -- а) СВОБ @ BLOCK UPDATE ;
\ Переопределим ЭЛЕМЕНТ так, чтобы ИСПОЛЬЗОВАННЫЕ пропускались:
: ЭЛЕМЕНТ ( i -- а)
1+ 2* 1024 /MOD СВОБ @ + BLOCK + UPDATE ;
: НЕТ-ИСП 0 ИСПОЛЬЗОВАНО ! ;
НЕТ-ИСП
: ПОМЕСТИТЬ ( n --) ИСПОЛЬЗОВАНО @ ЭЛЕМЕНТ ! 1 ИСПОЛЬЗОВАНО +! ;
: ВНЕСТИ ( n1 n2 --) SWAP ПОМЕСТИТЬ ПОМЕСТИТЬ ;
: ТАБЛИЦА CR ИСПОЛЬЗОВАНО @ 0 ?DO I 8 МOD 0= IF CR THEN
I ЭЛЕМЕНТ @ 8 .R LOOP CR ;
ГЛАВА 11
1.
: ЗАГРУЗКА ( n --) CREATE , DOES> ( --) @ LOAD ;
2.
: ОСНОВАНИЕ. ( n --) CREATE ,
DOES> ( n --) @ BASE @ SWAP BASE ' SWAP . BASE ! ;
3.
: МНОГО ( a --) CREATE ,
DOES> ( --) @ SWAP 0 ?DO DUP EXECUTE LOOP DROP ;
' CR МНОГО CRS
4 CRS
4.
: TURNE [COMPILE] DO ; IMMEDIATE
: RETURNE [COMPILE] LOOP ; IMMEDIATE
: ПОПЫТКА 10 0 TURNE I . RETURNE ;
5.
: ЦИКЛЫ ( #раз --)
>IN @ SWAP 0 DO DUP >IN ! INTERPRET LOOP DROP ;
6.
: STAR * 42 EMIT ;
: .РЯД ( b --) \ вывод звездочки на каждый бит из байта
CR 8 0 DO DUP 128 AND IF STAR ELSE SPACE THEN
2* LOOP DROP ;
VARIABLE ШАБЛОН
: БИТ ( t=не-пробел --)
1 AND ШАБЛОН @ 2* + ШАБЛОН ! ;
: ЗВЕЗДЫ>БИТЫ ( а -- b)
0 ШАБЛОН ! 8 OVER + SWAP DO I С@ BL <> БИТ LOOP
ШАБЛОН @ ;
: ЧТ-РЯД ( -- b)
ASCII ! WORD COUNT + 8 - ЗВЕЗДЫ>БИТЫ ;
: ФОРМА CREATE 8 0 DO ЧТ-РЯД С, . LOOP
DOES> 8 OVER + SWAP DO I С@ .РЯД LOOP CR ;
ФОРМА .L XXX_____!
_X______!
_X______!
_X______!
_X______!
_X______!
_X_____X!
XXXXXXXX!
ФОРМА .В ХХХХХХХ_!
_X_____X!
_X_____X!
_XXXXXX_!
_X_____X!
_X_____X!
_X_____X!
XXXXXXX_!
ГЛАВА 12
1.
: ширина ( смещение длина -- нов-смещение )
CREATE OVER , DUP , + ;
13 \ начальная позиция внутри записи
16 ширина фамилия
12 ширина имя
24 ширина работа
12 ширина телефон
CONSTANT /ЗАПИСЬ ( количество байт на запись ) EXIT
По приведенным выше правилам компилируются одинаковые структуры. "ширина" является определяющим словом, которое хранит на стеке текущую позицию внутри поля. Для каждого паля "ширина" компилирует смещение и длину, а затем их складывает, чтобы получить следующее смещение. После того, как определится "телефон", полученное а результате значение является длиной всей записи и превращается в константу /ЗАПИСЬ .
2.
: вызвать ( имя ( --)
имя запомнить ВВЕРХ -НАЙТИ IF ОТСУТСТВУЕТ
ELSE CR .ИМЯ телефон .ПОЛЕ THEN ;
Gudleifr- Admin
- Сообщения : 3403
Дата регистрации : 2017-03-29
Страница 2 из 2 • 1, 2
Похожие темы
» Броуди. Способ мышления - ФОРТ
» Баранов, Ноздрунов. Язык ФОРТ и его реализации. 1988
» Растригин. С компьютером наедине. 1990
» Брябрин. Программное обеспечение персональных ЭВМ. 1990
» Приложение. В мире науки. Занимательный компьютер. 1983-1990
» Баранов, Ноздрунов. Язык ФОРТ и его реализации. 1988
» Растригин. С компьютером наедине. 1990
» Брябрин. Программное обеспечение персональных ЭВМ. 1990
» Приложение. В мире науки. Занимательный компьютер. 1983-1990
Страница 2 из 2
Права доступа к этому форуму:
Вы не можете отвечать на сообщения