Forth и другие саморасширяющиеся системы программирования Locations of visitors to this page
Текущее время: Пт мар 29, 2024 01:13

...
Google Search
Forth-FAQ Spy Grafic

Часовой пояс: UTC + 3 часа [ Летнее время ]




Начать новую тему Ответить на тему  [ Сообщений: 49 ]  На страницу 1, 2, 3, 4  След.
Автор Сообщение
 Заголовок сообщения: Как разработать кросс-компилятор Форта для микроконтроллеров
СообщениеДобавлено: Ср сен 02, 2009 11:10 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
Как разработать кросс-компилятор Форта для микроконтроллеров

Форт для микроконтроллеров на протяжении уже долгих лет остается подобным птице Феникс — экзотическое, красивое и вечно возрождающееся создание. Крайняя редкость использования не в последнюю очередь является следствием неимоверной гибкости на грани аморфности, когда буквально с первых шагов работы с конкретной версией Форта ее можно модифицировать до потери совместимости не только с другими версиями, но с ней самой в другом варианте модификации. Однако простота правил, заложенных в язык, и простота методов разработки самого Форта приводит к тому, что поток новых реализаций не прекращается с момента появления языка в 1970 году. Что важно для нас, многие из этих реализаций являются разработками единственных авторов, поэтому написать такое в одиночку более чем реально.

Что следует отметить из особенностей Форта? Во-первых, это конкатенативная компиляция. Конкатенация — это «склеивание», когда фрагменты кода просто пишутся один за другим, без перекрестных связей друг с другом. Это позволяет сопоставить такие фрагменты с символическими обозначениями, и при компиляции оказывается достаточным разбирать текст программы на такие кусочки текста (с точки зрения теории их можно назвать лексемами или токенами), и после получения каждого кусочка немедленно вставлять в программу вызов связанного с ней кода. Например, если для токенов proc1, proc2 и proc3 написаны подпрограммы в машинных кодах, начинающиеся с адресов 123, 456 и 789, то текст на Форте «proc1 proc2 proc3» должен быть скомпилирован в последовательность команд call 123 call 456 call 789. Для чисел («непосредственных операндов») здесь делается исключение, поскольку на все многообразие чисел нельзя написать подпрограмм. Как числа обрабатываются компилятором, мы разберем чуть дальше, но с ними связана вторая особенность Форта, а поэтому сейчас будет «во-вторых».

Во-вторых, при конкатенативной компиляции естественным решением будет постфиксная запись выражений (сначала аргументы, потом операция с ними). Дело в том, что конкатенативная компиляция означает, что, скомпилировав команду самой операции, мы уже не должны возвращаться к ней, вписывая неизвестный пока операнд. Поэтому вместо A + B мы пишем A B +, получая очевидное решение в виде call A call B call +. Имеется в виду, что команды call переходят на соответствующие указанным токенам подпрограммы. Из-за унифицированного порядка организации подпрограмм Форт не использует деление на зарезервированные слова, переменные, константы, идентификаторы и т.д. - все токены называются словами. Ну а поскольку при постфиксной записи может накопиться немалое число уже принятых операндов, которые надо где-то хранить, еще одним естественным приемом является стековая машина. Надо сказать, что для МК она не является абсолютно обязательным элементом, о чем речь также пойдет ниже, однако стек — отработанное в практике программирование решение, и его применение в Форте продиктовано вполне определенными соображениями, а именно — достижением максимальной простоты компилятора.

Для микроконтроллеров в силу самых разных соображений может оказаться весьма полезным кросс-компилятор Форта — т.е. программа, которая обрабатывает текст на Форте на обычном PC, но компилирует код, который должен работать на МК. Разработка кросс-компилятора Форта для МК — достаточно интересное и своеобразное занятие, которое позволяет разработчику не только обеспечить себя собственным инструментом, но и реализовать различные варианты построения компилятора, от наиболее быстродействующего (настолько, насколько это позволяет подход, принятый в Форте) до наиболее компактного. Поскольку в самом Форте для PC уже есть многие механизмы обработки текста, нужные нам для создания кросс-компилятора, рассмотрим порядок разработки Форта для МК с помощью Форта для PC. Фраза «готовые примеры скачайте там-то» здесь встречаться не будет, поскольку основная цель данного материала — помочь разобраться (а не дать готовое решение, сопровождаемое бодрыми комментариями о том, как все хорошо).

Что нам понадобится для работы. Во-первых, собственно МК. Желательно, чтобы он был в наличии «вживую», а не в виде программного симулятора, и чтобы имелась возможность загрузить в него «мгновенный снимок» памяти. Зачем нужен мгновенный снимок? Потому что в процессе разработки кросс-компилятора мы в конце концов получим массив, содержащий образ памяти нашего МК. Поэтому важно, чтобы вот такой «сырой» образ, без заголовков, таблиц и прочих структур данных, мог быть один в один перенесен в память МК и запущен.
Во-вторых, нужен транслятор Форта для PC. Какой — практически все равно. Я реализовывал кросс-компиляторы на трех различных версиях Форта, и ни в одном случае не испытывал принципиальных неудобств.
В-третьих, необходима документация на систему команд ядра выбранного МК. Также нужен startup код, если он должен выполнять неочевидные вещи, которые компилируются стандартными для этого МК компиляторами, а вот мы по незнанию можем их не скомпилировать, и в итоге ничего не получится. Сюда обычно относится настройка критичной периферии, такой как встроенные контроллеры памяти и средства связи с PC.

Итак, начинаем. Условимся, что мы разрабатываем наиболее простой вариант кросс-компилятора, создающий машинный код для нашего МК. Договоримся также, что МК либо имеет гарвардскую архитектуру (раздельное пространство кода и данных), либо разделить код и данные можно искусственно — поместив их в разные устройства памяти. Зачастую оно так и есть, поскольку МК имеют флеш-память (достаточно большого объема), которую мы используем для хранения программы, и статическое ОЗУ (а его объем сравнительно небольшой), где естественно хранить данные. Кросс-компилятор не будет помещать в выходной код элементы Форт-транслятора, т.е. ничего лишнего в памяти МК не окажется, но самостоятельно принимать от других устройств программы на Форте и транслировать их он потом не сможет.

Начинаем мы с создания массивов, где будут храниться образы памяти целевой программы. Посмотрим документацию на предмет размеров памяти МК, и напишем нечто вроде:

Код:
32768 CONSTANT CODESIZE
1024 CONSTANT DATASIZE

CREATE CODE[] CODESIZE ALLOT
CREATE DATA[] DATASIZE ALLOT
VARIABLE C^
VARIABLE D^

Что у нас получилось? Мы задали константы, определяющие размеры областей памяти МК. Затем создали массивы CODE[] и DATA[] (квадратные скобки необязательны, но они рекомендуются для создания читаемого стиля). Наконец, объявили переменные-указатели, которые будут хранить смещения от начала массивов. Не забываем, что компиляция у нас будет конкатенативная, то есть код будет помещаться байт за байтом, поэтому достаточно помнить смещение первого свободного байта.
Теперь попробуем написать минимальный управляющий код.

Код:
: START
  0 C^ !
  0 D^ !
;

: ORG \ addr –
C^ !
;

: TC-C, \ byte -
  CODE[] C^ @ + C!
  C^ @ 1 + C^ !              \ в Форте часто можно написать здесь 1 C^ +!
;

Что получилось? START просто инициализирует указатели, ORG принудительно задает место, с которого будет продолжаться компиляция, а TC-C, составлено из двух частей. Запятая в Форте переносит число со стека в память (просто «как есть», т.е. вписывает это значение по первому же свободному адресу, передвигая указатель). Префикс C означает Char, а TC является сокращением от Target Compilation. Все вместе это означает «снять число со стека и перенести его на первое же свободное место в образе памяти целевой системы». Теперь мы уже можем создать программу, зная нужные нам машинные коды:

Код:
START
0 ORG
12 TC-C,
23 TC-C,
45 TC-C,

Пока не очень удобно. Кстати, вместо TC у меня прижилось слово ZC,. Первым целевым процессором был Z80, от него и осталась буква Z. Конечно же, Форт не накладывает никаких ограничений на имена слов, поэтому тут допустим определенный произвол, если он помогает правильно осмысливать программу.

Идем дальше. Для МК есть собственный вариант «Hello, world» - помигать светодиодом. Это намек на то, что заниматься компиляцией пересылок «регистр-регистр» и арифметических команд — несколько тупиковое занятие, в том смысле, что только из них показательную программу не составить. Гораздо полезнее посмотреть, как компилировать программу, состоящую из загрузки константы в регистр, отправке ее в какой-либо порт, и переходе на начало. Если мы выберем соответствующий порт, у нас загорится светодиод на плате с МК, а это значит, что компилятор все сделал правильно. Итак, смотрим в систему команд и пытаемся найти там что-либо простое. Здесь и далее коды операций будут совершенно абстрактные, и не относящиеся к какому-то конкретному процессору.

Код:
: NOP 0 TC-C, ;


Собственно, это уже элемент кросс-ассемблера. Если мы в предыдущем фрагменте кода ставили TC-C, «руками» то сейчас достаточно написать NOP, и код 0 сам окажется в массиве, отвечающем за память программ. Очевидно, что все безоперандные команды можно сделать таким образом. Посмотрим документацию еще немного, и обнаруживаем, что у нас в компиляторе оказалось приличное количество кодов.

Код:
: CLI 12 TC-C, ;
: STI 34 TC-C, ;

И так далее. Кстати говоря, если уж у нас все равно не будет полного совпадения с существующими форматами ассемблера, можно немного украсить синтаксис... а заодно и существенно облегчить себе жизнь.

Код:
: A->B 56 TC-C, ;
: A++ 57 TC-C, ;
: A+=B 58 TC-C, ;

Что у нас получилось сейчас? А мы попросту заменили команды с операндами на безоперандные варианты. Первая строка — это аналог команды пересылки, которая записалась бы в варианте mov b, a (или mov %a, %b, смотря какой порядок операндов принят). Если мы посмотрим, какой код соответствует такой команде, то его можно вписать прямо в наш компилятор, назначив приятное глазу имя. Далее идет команда инкремента, и наконец — сложение регистров A и B (напоминаю, что процессор — абстрактный).

Вообще-то подобные команды могут быть реализованы и иначе. Здесь следует привести конкретный пример — процессор i8080 (580ВМ80). Для него команда MOV имела вариант 01xxxyyy, где xxx и yyy — коды регистров, от 0 до 7. Если мы теперь зададим константы для регистров A, B, C, D, E, H, L, а потом еще для слов «A,» «B,» и так далее (разница — в запятой!), то потом можно будет «мимикрировать» под стандартный ассембер с помощью вот такой команды MOV

Код:
: MOV 8 * + 64 + TC-C, ;

A, B MOV

Этот пример (как и полный ассемблер для 580ВМ80, потрясающе компактный) описан в обязательной для прочтения книге Баранова и Ноздрунова «Язык Форт и его реализации». Как он работает? Слова A, и B оставят на стеке по одному числу. Верхнее умножится на 8, а это есть сдвиг на три разряда влево. Теперь мы его складываем с тем, которое под ним, и у нас уже получается двоичный код xxxyyy. Останется поставить единичку в 6-м разряде, добавив к числу 64. С тем же успехом у нас сработает код 2 3 MOV, поскольку то, что мы принимаем за имена регистров, на самом деле просто константы, отвечающие за эти регистры в машинном коде.
Почему мы так не сделали в нашем примере? В действительности, по многим причинам. Первая — для практического использования кросс-компилятора необходим контроль ошибок. Что будет с нашим кодом, если вместо чисел от 0 до 7 на стеке окажется 20? Неважно, случайно или намеренно. Вторая, и более важная — не все процессоры имеют такой уникальный для каждой разновидности команд синтаксис ассемблера, как 580ВМ80. У него MOV — это пересылка регистр-регистр, и только она. Например, загрузка в регистр числа имеет обозначение MVI (от Move Indirect), и так далее, поэтому ошибиться, приняв номер регистра за число, здесь невозможно. А вот в x86 любая команда пересылки имеет имя MOV, и тут уже необходимо серьезно задуматься о контроле типов операндов. Кстати, уже в Z80 все виды пересылок выполнялись командой LD (от Load), что уже заставляет контролировать типы. Это возможно, но способно увести нас в необязательные дебри, а нам хочется побыстрее увидеть работающий код. Поэтому, пока это возможно, превращаем команды в безоперандные. Кстати, необязательно набирать сотни строк кода, реализуя в компиляторе все сочетания операндов, какие только предлагает система команд. Для запуска нужно очень немного. Более того, я могу сослаться на подобный ассемблер для x86, в котором присутствуют даже такие слова, как EAX->[EBX+ECX*2]. Разумеется, там присутствуют далеко не все команды, а если говорить более точно, то лишь малая часть. Тем не менее, 700 строк Форта (около 15 кб) в свое время позволили написать 32-битную реализацию Форта для защищенного режима.

Резюмируя эту часть: опять-таки смотрим в документацию и выписываем команды, которые можно привести к безоперандному виду. В первую очередь, это команды «регистр-регистр». Теперь у нас уже должны появиться десятки слов в нашем целевом ассемблере.

Но мы приближаемся к чуть более сложной проблеме — а как, например, поместить в регистр число? Написать слова 0->A 1->A 2->A и так далее? Конечно, если постараться, да и автоматизировать генерацию таких строчек, то для 8-разрядного процессора окажется всего-навсего 256 вариантов загрузки на регистр. Но все равно громоздко, да и непонятно, а что же делать для 16- и 32-разрядных операндов??? Ведь 64к, а тем более 4Г вариантов, это, извините за каламбур, совсем не вариант.
Хорошо, смотрим в документацию. И видим там, что никаких особенных чудес вобщем-то нет. Команда загрузки числа в регистр представляет собой какой-то определенный код, за которым потом помещается загружаемое число. Допустим, что для загрузки числа 12 в регистр A мы должны скомпилировать коды 62 12, где 62 — это код команды. Тогда мы можем опять немного украсить синтаксис ассемблера.

Код:
: ->A \ x –
  62 TC-C,
  TC-C,
;


Сначала мы заносим в память код 62, а потом... какое-то число? Посмотрим на комментарий (стековую нотацию) нашего слова — там на стеке должен находиться некий x. Например, если мы напишем 5 ->A, то сначала при работе слова скомпилируется 62, а потом TC-C, начнет забирать число со стека, где так и лежит 5. Значит, в коде МК окажутся числа 62 и 5. Как же обеспечить загрузку 12? Очевидно, строкой 12 ->A. И ведь это уже решение проблемы! Нам осталось еще немного почитать документацию и выписать прочие команды, которые требуют операнды. Кстати, именно операнды, а не один операнд — смотрим на такую запись.

Код:
12 23 ->[A+OFFSET]


Такое слово заберет со стека два операнда. Да, OFFSET немного непривычно, зато результат сразу.

Теперь мы подходим к немного нетривиальному механизму кросс-компилятора — переходам по меткам. Нет, конечно же, мы и переходы можем сделать в стиле 125 JMP. Вот только вычислять вручную адреса конкретных команд очень и очень трудоемко. Крайне хотелось бы иметь метки. Попробуем и тут обойтись минимальными усилиями.

Код:
1000 CONSTANT MAXLABELS
CREATE LABEL[] MAXLABELS 4 * ALLOT


Создаем массив, в котором на каждую из 1000 возможных меток выделено по 4 байта. Умножить на 4 можно еще словом CELLS, оно правильнее с точки зрения стилистики, но ведь про него еще нужно знать. Кстати, тут можно было умножать и на 2 — главное, чтобы в выделенное место влезал адрес.
Теперь пишем так:

Код:
: LABEL: \ n –
  4 * LABEL[] +  \ на стеке адрес n-й ячейки массива
  C^ @ SWAP !  \ пишем туда текущее значение указателя свободного байта
;

: LABEL \ n – a
  4 * LABEL[] + @
;

Скорее пример!

0 ORG
NOP                      \ адрес = 0
12 ->A                   \ эта команда, видимо, 2 байта, и займет адреса 1 и 2
1 LABEL:             \ теперь в массив меток запишется адрес 3 для метки с номером 1
1 LABEL JMP     

После исполнения 1 LABEL на стеке окажется ранее записанный для этой метки адрес. В нашем случае это 3. Теперь нам останется реализовать JMP таким образом, чтобы это слово снимало со стека адрес, по которому необходимо сделать переход.
Да, это и непривычно, и как-то чрезмерно просто. А что будет, например, если мы напишем 23 JMP? Ничего правильного не будет, потому что метка 23 не была определена ранее. А уж если написать 10000 LABEL, то в процессе вычисления адреса программа вылезет далеко за пределы массива, с неопределенными последствиями. Хорошо, диапазон можно и проверять, а вот что делать с переходами вперед? Придется чуть посложнее.

Код:
CREATE FORWARD-ADDR[] 1000 4 * ALLOT
CREATE FORWARD-LABEL[] 1000 4 * ALLOT


Почему тут два массива? А потому, что на каждую метку может быть несколько ссылок вперед. Другими словами, в программе может несколько раз встречаться попытка перейти к метке 1, прежде чем встретится метка с этим номером, и станет понятно, куда же мы так активно стремились. Поэтому в один массив мы будем писать адреса, на которых мы «споткнулись», обнаружив, что пока не знаем, куда идти, а в другой — на какую метку мы хотели пойти. Для такой операции придется завести и другое слово.

Код:
VARIABLE FREE-SLOT

: FORWARD  \ n –
  FIND-FREE-SLOT \ внимание! Это еще не описано
  FREE-SLOT  @ 4 * FORWARD-LABEL[] + ! \ сняли со стека номер метки, к которой хотим перейти
  C^ @ FREE-SLOT  @ 4 * FORWARD-ADDR[] + ! \ запомнили адрес, где это произошло
  C^ 2 + C!  \ зарезервировали место для адреса
;


Что такое FIND-FREE-SLOT? Это слово, которое лежит несколько в стороне от основной линии изложения, и его желательно реализовать самостоятельно. Оно должно записать в переменную FREE-SLOT индекс ячейки, которую можно занять для новой ссылки вперед. В принципе, можно и просто перемещать указатель, каждый раз занимая новую ячейку, и при памяти современных PC мы еще нескоро добьемся их исчерпания. Однако, если уж подходить правильно, как только мы определим метку с нужным нам номером и впишем все адреса на их места, все записи, относящиеся к этой метке, будут нам не нужны. Поэтому можно инициализировать массив FORWARD-LABEL[] числами -1, считая этот номер признаком свободной ячейки. Слово FIND-FREE-SLOT должно будет пройти по массиву и вернуть номер ячейки, в которой лежит -1.
Что теперь с этим делать, когда мы наконец объявим метку с нужным нам номером? Необходимо будет произвести разрешение метки (в русском языке слово выглядит двусмысленным, но оно происходит не от enabling, а от resolving, т.е. мы не «даем позволение», а «решаем задачу»). Представим, что в какой-то момент мы определили метку, и теперь мы знаем, чему же равен искомый адрес (подсказка для тех, кто забыл, с чего мы начали — для этого достаточно выполнить C^ @). Делаем так.

Код:
: ?RESOLVE-REF \ n --
1000 0 DO
  FORWARD-LABEL[] I 4 * + @ OVER = IF \ в этом слоте находится нужная нам метка?
  C^ @                                           \ вот наш адрес
  FORWARD-ADDR[] I 4 * + @  \ а вот место, куда мы его хотели вписать, но еще не знали
  !                                                   \ пишем
  -1 FORWARD-LABEL[] I 4 * + \ теперь нам этот слот уже не нужен, помечаем его как незанятый
  THEN
LOOP
DROP
;


Теперь необходимо вернуться немного назад, к слову LABEL. Дело в том, что когда мы определяем метку, следует проверить, не было ли на нее ссылок. Теперь-то адрес стал известен, так вдруг его кто-то с нетерпением ждет? Следовательно, в конце слова LABEL необходимо добавить ?RESOLVE-REF. Соответственно, и LABEL следует переставить по тексту ниже, чтобы не получить ошибку «слово не определено». А рассматривали мы эти механизмы в порядке возрастания сложности, поэтому и LABEL оказались в наши примерах перед более сложным FORWARD.

В кросс-компиляторах я предпочитаю использовать следующий формат:
Код:
1 LABEL:
JMP 1 LABEL
JMP 2 FORWARD
2 LABEL:


Это означает то, что слово JMP не пытается снять со стека адрес, а просто компилирует машинный код команды перехода. А вот уж потом слово LABEL само допишет нужный адрес (в нашем примере получилось немного не так, но достаточно дописать TC-W, - это компиляция word, или, если речь о 32-разрядных адресах, TC-,). Соответственно, слово FORWARD передвинет указатель свободного места, зарезервировав его под адрес, который будет туда записан словом ?RESOLVE-REF, которое, в свою очередь, вызовется из LABEL:

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


Последний раз редактировалось Hishnik Чт сен 03, 2009 21:13, всего редактировалось 1 раз.

Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Ср сен 02, 2009 22:44 
Не в сети
Moderator
Moderator
Аватара пользователя

Зарегистрирован: Чт май 04, 2006 00:53
Сообщения: 5062
Откуда: был Крым, теперь Новосибирск
Благодарил (а): 23 раз.
Поблагодарили: 63 раз.
остается проблема в том, что переходы бывают длинные и короткие. А нам в предложенном варианте придется пользоваться только длинными переходами... В смысле, это проблема если места уж совсем мало и на счету каждый байт 8)

_________________
Мне бы только мой крошечный вклад внести,
За короткую жизнь сплести
Хотя бы ниточку шёлка.
fleur


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 00:11 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
mOleg писал(а):
остается проблема в том, что переходы бывают длинные и короткие. А нам в предложенном варианте придется пользоваться только длинными переходами... В смысле, это проблема если места уж совсем мало и на счету каждый байт

Даже в представленном максимально упрощенном подходе существует немалый резерв. Начнем с того, что не все МК имеют разные варианты переходов, ну да ладно, предположим, что короткие переходы все же существуют. Тогда половина проблемы все равно отпадает сразу, поскольку переход назад компилируется с уже известным смещением. Простая проверка его величины позволяет в один IF выбрать длинный или короткий вариант.
Остается вопрос с переходами вперед. Здесь можно начать с того, что команда JMP может быть представлена также в вариантах LJMP и SJMP (long и short). Далее мы ставим SJMP, где только возможно, помечая размер в таблице ссылок вперед, и если смещение окажется слишком большим, выводим ошибку, заставляя программиста заменить этот переход на LJMP. Альтернатива - многопроходная компиляция, которая сначала собирает адреса меток, а уже на следующем проходе пытается понять, какой вариант использовать. Прекрасный вариант для долгостроя! :))


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 06:19 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 13:19
Сообщения: 3565
Откуда: St.Petersburg
Благодарил (а): 4 раз.
Поблагодарили: 72 раз.
Надо таки начинать с длинного перехода, а не с короткого, чтобы программа начала работать.
А замена на короткие - это та самая оптимизация, которую надо делать "потом".

Иначе "однопроходность" окажется фикцией, если после первого автоматического прохода надо проходить исходники еще раз вручную, а потом опять автоматом...

_________________
С уважением, WingLion
Forth-CPU . RuF09WE
Мой Форт
Отсутствие бана это не заслуга юзера, а недоработка модератора (с)


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 09:52 
Не в сети
Аватара пользователя

Зарегистрирован: Вт сен 11, 2007 11:07
Сообщения: 187
Благодарил (а): 0 раз.
Поблагодарили: 1 раз.
досовским х86 ассемблерам ручная разметка никак не мешала и всё работало так, как говорил тигра.
Код:
.586
.model flat
.code
start: jmp go
db 100 dup('*')
go:
end start

однопроходный режим:
    1 .586
    2 00000000 .model flat
    3 00000000 .code
    4 00000000 EB 67 90 90 90 start: jmp go
    5 00000005 64*(2A) db 100 dup('*')
    6 00000069 go:
    7 end start
многопроходный режим:
    1 .586
    2 00000000 .model flat
    3 00000000 .code
    4 00000000 EB 64 start: jmp go
    5 00000002 64*(2A) db 100 dup('*')
    6 00000066 go:
    7 end start
все пользовались и никому ничего не мешало, а вот как выглядит переход назад (когда длина заранее известна) в однопроходном режиме:
    1 .586
    2 00000000 .model flat
    3 00000000 .code
    4 00000000 start:
    5 00000000 64*(2A) db 100 dup('*')
    6 00000064 EB 9A go:jmp start
    7 end start
короткая же форма перехода выбиралась и фиксировалась вручную как jmp short label,
при этом возможные ошибки выглядели так:
    **Error** 0.asm(4) Relative jump out of range by 00ADh bytes


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 12:22 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
WingLion писал(а):
Надо таки начинать с длинного перехода, а не с короткого, чтобы программа начала работать.
А замена на короткие - это та самая оптимизация, которую надо делать "потом".

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

garbler писал(а):
короткая же форма перехода выбиралась и фиксировалась вручную как jmp short label,

Именно! На то он и ассемблер.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 15:29 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
WingLion писал(а):
А замена на короткие - это та самая оптимизация, которую надо делать "потом".

Кстати, если начинать с длинных переходов, то можно будет выдать в листинге те места, где длинный переход оказался избыточным. Впрочем, это все равно мелочи, экономия на переходах обычно дает выигрыш в байты. Как правило, в начале программы на стартовом адресе идет длинный переход вперед, на разновидность main-а. Много call-ов на уже определенные слова, которые являются переходами к уже известным адресам. Периодически условные переходы вперед на IF-ах, но их можно полагать короткими.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 19:37 
Не в сети

Зарегистрирован: Вт май 09, 2006 12:31
Сообщения: 3438
Благодарил (а): 5 раз.
Поблагодарили: 16 раз.
Цитата:
Кстати, если начинать с длинных переходов, то можно будет выдать в листинге те места, где длинный переход оказался избыточным.

Хм, длинные и короткие переходы ...
Не сделает ли правильно организованный инлайн большинство длинных переходов короткими (между словами - внутри слов)
:wink:
Цитата:
В смысле, это проблема если места уж совсем мало и на счету каждый байт

Если места очень мало то не окажутся ли все переходы по факту короткими?


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 19:40 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
вопрос писал(а):
Хм, длинные и короткие переходы ...
Не сделает ли правильно организованный инлайн большинство длинных переходов короткими (между словами - внутри слов)

Внимательно читаем приведенный материал. Речь идет об организации кросс-ассемблера для произвольно выбранного МК, а не об "инлайнах". Умея компилировать машинные коды МК, можно говорить о дальнейших шагах. Инлайна пока нет как такового, хотя бы потому, что ассемблерный уровень не подразумевает деление на инлайн и не-инлайн. У него все инлайн.
вопрос писал(а):
Если места очень мало то не окажутся ли все переходы по факту короткими?

Вполне возможно, что и окажутся. Только чего гадать-то? :)


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 19:55 
Не в сети

Зарегистрирован: Вт май 09, 2006 12:31
Сообщения: 3438
Благодарил (а): 5 раз.
Поблагодарили: 16 раз.
Цитата:
Внимательно читаем приведенный материал.

Да, познавательно
Цитата:
ассемблерный уровень не подразумевает деление на инлайн и не-инлайн. У него все инлайн.

Я о статистике - если программа, которую потом будут ассемблить, будет составлена с применением опр. техник - инлайна, то встречаемость длинных переходов станет ниже
Цитата:
Разработка кросс-компилятора Форта для МК — достаточно интересное и своеобразное занятие, которое позволяет разработчику не только обеспечить себя собственным инструментом, но и реализовать различные варианты построения компилятора, от наиболее быстродействующего (настолько, насколько это позволяет подход, принятый в Форте) до наиболее компактного.
Выделенное - любопытно


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 20:08 
Не в сети

Зарегистрирован: Вс июн 21, 2009 19:11
Сообщения: 81
Откуда: Н.Новгород
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Хищник писал(а):
WingLion писал(а):
А замена на короткие - это та самая оптимизация, которую надо делать "потом".

экономия на переходах обычно дает выигрыш в байты. Как правило, в начале программы на стартовом адресе идет длинный переход вперед, на разновидность main-а. Много call-ов на уже определенные слова, которые являются переходами к уже известным адресам. Периодически условные переходы вперед на IF-ах, но их можно полагать короткими.

Вот в MSP, например, все CALL-ы короткие (10 бит, по-моему). Записываются одним словом.
А "длинный" - это MOV константы в PC. В 2 раза длиннее (2 слова), намного тормознее (лишнее обращение к памяти). Так что есть смысл по возможности использовать короткие.

Статья понравилась. Жду продолжения.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 21:07 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
вопрос писал(а):
Я о статистике - если программа, которую потом будут ассемблить, будет составлена с применением опр. техник - инлайна, то встречаемость длинных переходов станет ниже

Еще раз обращаю внимание на то, что это никакая не программа, а всего-навсего ассемблер. Он никоим образом не ответственен за то, что на нем будут компилировать - программист может принудительно использовать неэффективные конструкции, и никто ему это не запретит. В этом смысл ассемблера - замена мнемонических обозначений команд на строго соответствующие им машинные коды. Так что будем мы использовать какие-то техники или не будем - это
1) Проблема совершенно другого уровня - того подхода, который мы используем при построении прикладных программ. А то получается обсуждение текстового редактора на уровне "а как в нем набирать "Войну и мир".
2) Проблема, которая может и объективно не иметь решения - например, когда переход, хоть тресни, а в короткую форму не влезает.

вопрос писал(а):
(настолько, насколько это позволяет подход, принятый в Форте) до наиболее компактного.
Выделенное - любопытно

Опять-таки, в тексте есть обсуждение. Компиляция - конкатенативная (за исключением вялотекущих замечаний Михаила, что он может написать любой оптимизатор, вот только пока получаются полуфабрикаты), а значит, программа состоит преимущественно из вызовов подпрограмм.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Чт сен 03, 2009 23:38 
Не в сети

Зарегистрирован: Вт май 09, 2006 12:31
Сообщения: 3438
Благодарил (а): 5 раз.
Поблагодарили: 16 раз.
Цитата:
Опять-таки, в тексте есть обсуждение. Компиляция - конкатенативная (за исключением вялотекущих замечаний Михаила, что он может написать любой оптимизатор, вот только пока получаются полуфабрикаты), а значит, программа состоит преимущественно из вызовов подпрограмм.
да, я не в тему, просто я сейчас пытаюсь понять, как возможен оптимизатор, именно если не выходить за рамки того, "насколько это позволяет подход, принятый в Форте", увидел фразу и ...
"Компиляция - конкатенативная " - это ведь как-раз возможность для оптимизатора - его действия более ... линейны - логика проще, хотя эффективность при этом значительно ниже


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Пт сен 04, 2009 00:31 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
вопрос писал(а):
да, я не в тему, просто я сейчас пытаюсь понять, как возможен оптимизатор, именно если не выходить за рамки того, "насколько это позволяет подход, принятый в Форте", увидел фразу и ...
"Компиляция - конкатенативная " - это ведь как-раз возможность для оптимизатора - его действия более ... линейны - логика проще, хотя эффективность при этом значительно ниже

Хех, можно подумать, я проявлю ложную вежливость и не начну ругать такой подход в хвост и в гриву! :))
Итак, по порядку и со вкусом. Мы чем занимаемся? Азами. Конечно, это азы не счета и письма, а как-никак системного программирования, но это все равно азы, и пока человек не пройдет стадию проб и ошибок, строить далеко идущие планы нет никакого смысла. В свою очередь, стадия проб и ошибок заканчивается, когда "из-под пера" выходит конструкция, которую с удовольствием принимают совершенно посторонние люди, ничего о Форте не знающие. А не когда в инете оказывается одной ссылкой больше. Конечно, можно встать в позицию "я лучше сразу напишу оптимизирующий компилятор", но это прекрасная по эффективности ловушка для себя самого. Я бы даже сказал, оптимальная ловушка :)) Потому что чем серьезнее взятая на себя задача, тем дольше можно пускать самому себе пыль в глаза, считая, что еще немного, и все будет в ажуре.
Почему так? Да по очень простой причине - разбираем проблему "на пальцах". Это то, что надо бы было сделать сразу, да и то, о чем я уже вскользь написал. Ладно, буду разжевывать.
Начнем с того, что термин "оптимальный" из-за его звучания и общеязыковой смысловой нагрузки часто ассоциируется с последовательностью "было не очень хорошо - что-то сделали - стало максимально хорошо". При этом любая программа с ярлыком "оптимизатор" подсознательно принимается чуть ли не за ИИ с навыками волшебника, который умеет вытаскивать безнадежный по параметрам код на недосягаемую высоту. Хотя достаточно начать с того, что замена длинных переходов на короткие оптимизацией не является. У нас нету возможного ухудшения отдельных показателей, за счет чего улучшаются интегральные характеристики программы. Мы просто-напросто меняем не вполне удачный код на более удачный, а оптимизацией это называть некорректно. Спрашивается, а что мешало поставить удачный код сразу?

Теперь по коду. Во-первых, что делать с ситуациями, когда короткий переход попросту не может быть использован? А с ними делать просто нечего. Вообще нечего. И никакой оптимизатор не спасет. Длинный переход в данном случае - это единственно возможный вариант, и разговор на этом прекращается, не начавшись.
Теперь повторяю ранее сказанное. Переходы бывают вперед и назад. То есть по неизвестным заранее адресам и по уже известным. Так вот, половина случаев (переходы назад, по уже известным адресам), проблемы не представляют, поскольку кросс-ассемблер даже в простейшем варианте компилирует наиболее короткий вариант перехода. Точка. Могу переформулировать - компилятор считает смещение по заранее известным данным, и, если он укладывается в короткий переход, ставит короткий переход.
Теперь про переходы вперед. В программах для МК в подавляющем большинстве случаев используются всего два типа переходов вперед.
Первый - это переход из стартового адреса МК на начало кода, в точку main(). Как правило, в процессе компиляции определений они размещаются с младших адресов, в результате адрес, с которого необходимо начинать исполнение, оказывается где-то далеко-далеко впереди, и это, как правило, длинный переход. То есть коротким тут и не пахнет. В любом случае, это один-единственный случай на всю программу.
Второй - это обход кода в структурах управления. В каких именно структурах - смотрим. IF ELSE THEN и BEGIN WHILE REPEAT. Собственно, все. IF компилирует условный переход на ELSE или THEN, ELSE компилирует безусловный переход на THEN. WHILE компилирует условный переход на REPEAT. Собственно, вот это и есть критическая точка, где необходимо принимать решение насчет конкретной реализации.
Вариант 1. Пишем Оптимизирующий Компилятор. Что бы это ни означало. Означать, вероятнее всего, будет выброс адреналина и осознание глобальности задачи, растягивающееся на годы вперед.
Вариант 2. Понимаем, что работать надо сейчас, и с конкретными проектами, а не через год-два-три с непонятно какими (когда, кстати, в МК уже будет настолько больше памяти, что наш потенциальный выигрыш от сверхинтеллектуальной замены длинных переходов короткими никого не будет волновать). Собственно, оба варианта действий более чем приемлемы на практике, тем более, что ассемблеры обычно так и работают
а) Везде длинные переходы, если они избыточно, пишем в лог, в конце лога сообщаем программисту, что в 10-килобайтной программе у него 100 байт лишних.
б) Везде короткие переходы, честно сообщаем об ошибке, предлагая руками поставить в этом месте длинный переход.
ВСЕ.

Если речь идет о других видах оптимизации, которые "вообще", то это на факультет прикладной математики.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения:
СообщениеДобавлено: Сб сен 05, 2009 01:08 
Не в сети
Administrator
Administrator
Аватара пользователя

Зарегистрирован: Вт май 02, 2006 22:48
Сообщения: 7960
Благодарил (а): 25 раз.
Поблагодарили: 144 раз.
Как разработать Форт для МК, имея форт-ассемблер

Продолжаем обеспечивать себя инструментальными средствами. Теперь (надеюсь), у нас есть ассемблер для выбранного МК, точнее, набор слов Форта, которые позволяют удобно компилировать код для него. С одной стороны, они не в точности совпадают со стандартным синтаксисом ассемблера, а с другой, стоит обратить внимание, что в нашем распоряжении имеется доступ к мощнейшей системе создания макросов. Можно группировать ассемблерные команды, придавая им читабельный и выразительный вид. Это, безусловно, плюс, который компенсирует получившуюся нестандартность. Далее посмотрим, как можно написать кросс-транслятор Форта для МК на таком ассемблере.
Сначала немного терминологии. В слове (иначе - словарной статье) Форта выделяют заголовок и тело. В заголовке располагается служебная информация, необходимая транслятору Форта для поиска и компиляции, а тело — это собственно код, без которого никуда. Например:

Код:
0    0 0     поле связи (link). Тут лежит адрес предыдущего поля связи. Это слово указывает «в никуда», потому что оно самое первое
2       3           это поле имени (name). Сначала идет байт длины
3       d           а теперь собственно байты символов — слово 'dup'
4       u
5       p
6       0           флаги. Они разные. Повсеместные — immediate, vocabulary и smudge, по биту на флаг
7    mov xx, yy          а это уже собственно код. Раз он по адресу 7, значит, для выполнения слова dup надо сделать call 7
8     поскольку это пример, я не знаю, сколько места займет код
9
10     ret      но закончится он командой ret
===========================
11    0  0    поле связи следующего слова. Тут тоже ноль, но в отличие от предыдущего линка, оно указывает не «в никуда», а на линк. Для большей показательности ниже приведен линк еще одного слова
13


22  ret
===========================
23   0 11       вот еще один линк. Он указывает уже на 11
25

Это один из примеров организации словарной статьи Форта, использующего машинный код. Тут, конечно, много мест, где можно что-то изменить. К примеру, можно хранить имена не в виде «счетчик, символы», а в виде ASCIIZ строки (завершаемой нулевым байтом). В ряде версий Форта к счетчику символов также «прицепляли» флаги, так, что пять младших разрядов счетчика кодировали собственно длину, а в старших размещались три указанных флага. Про флаги пока не будем, мы сейчас посмотрим, как это все соотносится с понятием «кросс-компилятор Форта».
Так вот, в глобальном смысле у нас есть два варианта. Либо мы кладем в МК всю словарную статью, либо только ее тело — то есть только машинный код. В первом случае МК сможет искать слова, то есть в нем можно будет реализовать самостоятельно работающий транслятор Форта, а во втором — трансляцию программы будет всегда делать PC, зато объем кода окажется существенно меньше. Очевидно, что если мы разрабатываем устройство, которое не имеет полноценной консоли, а из органов управления и индикации у него кнопки, переключатели, светодиоды и дисплеи, то оператору будет просто некуда ввести строку, которую он хотел бы исполнить. И таких задач достаточно много, явно больше, чем систем, где МК должен выполнять вводимые ему строки Форта в процессе эксплуатаци системы. Поэтому явно стоит посмотреть на простой вариант кросс-транслятора.
Еще раз определимся с задачей. Мы собираемся сделать так, чтобы строка вида

Код:
2 2 + .


приводила к созданию кода, загружаемого в память МК, который бы потом выводил на свой дисплей результат работы. В принципе, вывести что-то можно и на нашем форт-ассемблере, но это уже пройденный этап. Поэтому идем дальше.
Очень эффективная штука в Форте — конструкция CREATE DOES>. Сейчас мы посмотрим, как с ее помощью обеспечить кросс-компиляцию слов. Сначала о том, что это такое.
Вот как в Форте определяется константа:

Код:
: CONSTANT CREATE , DOES> @ ;
5 CONSTANT FIVE

То есть это даже не определение константы, а определение слова, создающего константы. Оно использует CREATE DOES>. Первое из них создает новое слово, с именем, выбираемым из входного потока. Самое главное, что тут следует понять, что CREATE «увидит» не запятую, а то, что будет после CONSTANT в процессе работы этого слова. В нашем случае это FIVE. Если бы мы выполнили просто CREATE FIVE, то слово FIVE клало бы на стек какое-то число. Гадать не будем, это просто значение указателя свободного места в памяти данных, которое возвращает слово HERE. Тут тоже нужно осмыслить тот факт, что HERE-то потом будет увеличиваться дальше, а вот FIVE так и будет класть то, что запомнит в процессе своего создания.
Все это хорошо, но у нас есть еще какой-то код после CREATE. Сначала идет запятая. Она переносит число со стека в память данных. Становится уже интересно. То есть создано FIVE, которое кладет на стек адрес ячейки, потом мы в эту ячейку кладем число 5 (запятая еще и указатель переместит), а потом работает слово DOES>, которое заставляет слово FIVE сделать переход на точку после DOES> и выполнить все вплоть до точки с запятой.
Опять смотрим по шагам. Итак, мы создали слово FIVE. Потом с помощью запятой «подложили» нужную нам константу в ту ячейку, на которую указывает FIVE. Потом сработает DOES> и перенаправит код в точку @ ; И откуда же @ будет забирать данные? Так из той ячейки, куда мы положили 5, и заберет!
И вот сейчас мы эти возможности будем эксплуатировать. Что нам, собственно, нужно? Чтобы в момент определения слова для МК оно запоминало бы свой адрес, а при упоминании этого слова — компилировало вызов на запомненный адрес.
Маленькая проблема — как назвать такое слово, которое будет определять слова для МК. Двоеточие уже занято, и его повторное определение создаст проблемы для транслятора. Проще всего, конечно, придумать другое слово — например, вместо пары : ; использовать function end-function.

Код:
: TC-CALL, <какие-то слова> ; компилирует переход по адресу, переданному на стеке
: TC-RET, 123 TC-C, ; компилирует ret (в предположении, что его код 123, т.е. тут нет никаких особых изысков)

: function CREATE C^ @ , DOES> @ TC-CALL, ;
: end-function TC-RET, ;


Разберем теперь слово function на примере вызова

Код:
function dup

end-function

В момент вызова function в переменной C^ был какой-то адрес. Там, собственно, всегда адрес первого свободного байта, с которого следует продолжать компиляцию. Итак, у нас сначала создастся слово dup, потом код C^ @ , «подложит» текущее значение C^ по адресу, который будет класть на стек dup при исполнении. А что будет созданное слово dup делать потом, ведь у нас еще DOES> есть? Сначала оно выполнит @ и положит тем самым запомненное ранее значение C^. То есть, собственно, то место, с которого началась компиляция кода для dup. Теперь мы делаем TC-CALL, и компилируем вызов на этот адрес — то есть вызов dup. Все, оказывается, совсем не сложно.
Что мы потеряли по сравнению с обычным интерактивным Фортом? А потеряли возможность вызова слов в режиме интерпретации — они же у нас только компилируются. Впрочем, для МК мы именно этого и хотим, так что ничего страшного не произошло.
Собственно, теперь мы уже можем писать базовые слова Форта для МК. Базовые — это те, которые «обертывают» собой стандартные куски кода. Стековые манипуляции, арифметика и логика, работа с памятью, обмен с портами и т.п. Однако нам надо подумать еще о двух пунктах.
Первый — это компиляция структур управления. Например, слово IF в Форте является словом немедленного исполнения, и транслятор не просто компилирует call IF, а в принудительном порядке исполняет его, а уж оно само вставляет в код нужный фрагмент. Соответственно, нам тоже надо будет сделать нечто вроде:

Код:
: if
12 TC-C,
34 TC-C,
;

Коды команд условны. Конкретно IF компилирует такую конструкцию, которая делает условный переход вперед, если на стеке данных находится ноль. Над самой конструкцией необходимо будет подумать отдельно, поскольку ее реализация зависит как от МК, так и от выбранными нами способами реализации стека, компиляции и прочего. Дело здесь не в конкретной команде, а в том, что в Форте не все исчерпывается простановкой команд call addr. Итак, структуры управления для МК — предмет отдельного рассмотрения.
Второй пункт — работа с числами. Например, мы уже сделали слово + для нашего МК, и теперь собираемся написать

Код:
function test
2 2 +
end-function

Но нам это ничего не дает, потому что 2 — это не слово, а число, и оно просто легло на стек Форта на хост-машине. В памяти МК будет просто call +, а вот числа туда не попадут. Что же делать? Проблема на самом деле не такая простая, как кажется на первый взгляд. Числа со стека хост-машины надо как-то выдирать. Кардинальное решение — скорректировать реализацию слова NUMBER в Форте для PC (ни более и ни менее). Но это сложно. Впрочем, я для своих трансляторов делал NUMBER векторным (можно назначить ему собственную версию), а специально для целей кросс-компиляции, которая выполняется мной не так уж редко, после помещения на стек каждого числа вызывается специальное слово DISPATCH-NUMBER, которое по умолчанию ничего не делает. Но если пишется кросс-компилятор, то DISPATCH-NUMBER компилирует в память МК такой код, который помещал бы на стек указанное ему число. Это называется компиляцией литерала, и кардинальным образом отличается от реализации слова «запятая». Запятая просто берет число со стека и пишет его в память данных. А вот «литерал» компилирует такой код, который в процессе исполнения будет помещать на стек Форта в МК указанную ему константу. Если имеется слово DISPATCH-NUMBER, то в function нужно добавить такое

Код:
['] TC-LITERAL, TO DISPATCH-NUMBER

а в end-function

Код:
['] NOOP TO DISPATCH-NUMBER

Тогда за пределами «скобок» function end-function все числа будут помещаться на стек нашего Форта для PC, а вот внутри них они будут компилировать в код, который разместит их на стеке МК.
Есть и еще один вариант, на уровне «чтобы запустилось». Придумать некий значок, который будет отправлять число на стек МК. Некрасиво, зато дешево и сердито.

Код:
function test
2 # 2 # +
end-function

В нашем примере слово # (это слово не Форт-МК, а Форта в нашем кросс-компиляторе) будет компилировать код, который поместит число на стек МК.

Теперь начинаем потихоньку приводить наш Форт в порядок. Надо организовать ему старт, почему бы и не словом START

Код:
VARIABLE STARTUP-REF
: START
  тут компилируется какой-то стартовый код, нужный для настройки периферии, без которого никуда
LJMP
C^ @ STARTUP-REF !
;

: MAIN()
С^ @ STARTUP-REF @ !
;

Чем мы таким занялись? В стартовом коде сделали нечто базовое, и тут же выполнили длинный переход далеко-далеко вперед. Потому что после START у нас начнутся слова Форта для МК, и будет их достаточно много. Пока не доберемся до «главного слова», которое ради пущей наглядности назовем MAIN() (да-да, прямо со скобками). И первым делом нам надо будет быстренько сказать, чтобы тот самый длинный прыжок вперед шел именно сюда. Текущий адрес у нас в C^, а в STARTUP-REF лежит адресок, который пока пустует и ждет адреса, где начинается MAIN(). Вот и вписываем. Это же можно сделать с помощью уже разобранного ранее механизма реализации переходов вперед, но этот переход уникальный в своем роде, поэтому я обычно его делаю отдельно.
Итак, подводим промежуточные итоги. Сейчас у нас есть возможность сделать массу кусков кода, поименовав их в стиле Форта... или в другом стиле. Отдельное внимание необходимо уделить компиляции структур управления, но это зависит от выбранной модели памяти и стека в МК, так что уточнять будем далее. Также отдельное внимание требуется числам, и это зависит уже от примененного нами Форта для PC. Далее мы посмотрим, как бы можно было реализовать стек для МК и простейшие структуры управления.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 49 ]  На страницу 1, 2, 3, 4  След.

Часовой пояс: UTC + 3 часа [ Летнее время ]


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9


Вы не можете начинать темы
Вы можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
phpBB сборка от FladeX // Русская поддержка phpBB