PDFORTH = Public Domain FORTH для PIC16F87X

8-битные микроконтроллеры PICmicro (ПИКи) от Microchip и совместимые, а также 16-битные PIC24 и 32-битные PIC32

Moderator: Shaos

User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

PDFORTH = Public Domain FORTH для PIC16F87X

Post by Shaos »

Задумал Публик-Домайный Форт для пиков, причём не просто кросс-компилятор (которых есть уже), а настоящую интерактивную Форт-систему, работающую через COM-порт непосредственно на пике, который способен сам себя программировать, например PIC16F870 (и далее на любом PIC16F87x). Делать думаю настоящий шитый код как последовательность CALL-ов, 16-битные вычисления, минимальный, но достаточный набор слов Форта ну и возможность заводить свои слова, с непосредственной компиляцией и программированием прямо в пик! :o

P.S. Обнаружил, что название "NedoForth" уже занято - поэтому пусть будет PDFORTH (Public Domain FORTH) т.к. по сути оно будет надстройкой над PDBLv1 :roll:
Last edited by Shaos on 07 Dec 2014 07:04, edited 4 times in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

~

Post by Shaos »

Регистр FSR будет указателем на стек (т.е. псевдорегистр INDF будет всегда ссылаться на вершину стека с младшим байтом двух-байтового слова), причём стек будет идти сверху-вниз (от старших адресов к младшим).

Чтобы положить слово на вершину стека, надо сделать декремент FSR, затем скопировать старший байт в INDF, потом снова сделать декремент FSR и скопировать младший байт в INDF, например нижеследующий код положит на вершину стека число 0x0102:

Code: Select all

decf FSR,f
movlw 1
movwf INDF
decf FSR,f
movlw 2
movwf INDF
Чтобы сбросить со стека число не читая (т.е. реализовать фортовское слово DROP), надо просто дважды инкрементировать FSR:

Code: Select all

incf FSR,f
incf FSR,f
Пока размышляю где держать имена откомпилированных фортовских слов - в EEPROM (которого всего 64 байта на PIC16F870) или прямо в коде - по два 7-битных символа в слове программной памяти:

Code: Select all

...
 RETURN ; from previous subprogram
 ; subprogram for "DROP"
 DATA 0x0244 ; (4<<7)|'D'
 DATA 0x294F ; ('R'<<7)|'O'
 DATA 0x2800 ; ('P'<<7)|0;
DROP:
 INCF FSR,f
 INCF FSR,f 
 RETURN
; next word subprogram...
Соответственно при компиляции просто программно ищем слова начиная с какого-то места в памяти после каждой инструкции RETURN (код 0x0008). Название слова будет всегда начинаться с количества символов в названии - это на всякий случай для проверки, скажем слова в нашей реализации Форта не смогут иметь названия длиннее 15 символов (что упаковывается в 8 пиковских слов).

P.S. Для упрощения декодирования можно ограничиться в названии фортовских слов только символами, цифрами и большими латинскими буквами - тогда каждую букву названия слова можно упаковать в 6 бит - при этом пиковское слово сможет иметь в младшем байте один символ как есть, а в старших 6 битах (напомню что тут пиковское словое - 14 битное) символ минус 0x20 (код пробела) - тогда сюда влезет ASCII диапазон 0x20...0x5F (т.е. без маленьких букв и символов `{|}~ что нас вполне устроит).

P.P.S. Ещё раз объясню процесс компиляции прямо на пике - чел вводит через терминал описание своего слова, которое компилируется прямо в память пика, при этом встречаемые слова (обычные, не специальные) ищутся по памяти и в коде заменяются вызовом соответствующих подрограмм - вобщем как-то так. А вот со специальными словами (CHAR IF REPEAT и т.д.) сложнее - тут компилятор должен их компилировать не CALL-ами...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Например подпрограмма, кладующая на вершину фортовского 16-битного стека один байт, будет выглядеть так:

Code: Select all

; put byte W to stack
PUTB:
 DECF FSR,F
 CLRF INDF
 DECF FSR,F
 MOVWF INDF
 RETURN
Тогда CHAR A может быть скомпилировано вот в такой код:

Code: Select all

 MOVLW 65 ; 'A'
 CALL PUTB
Числа меньше 256 могут также быть положены на стек с помощью той же подпрограммы PUTB, остальные же 16-битные числа (-32768...+32767) будут компилироваться в такую вот конструкцию:

Code: Select all

 DECF FSR,F
 MOVLW higher-byte
 MOVWF INDF
 DECF FSR,F
 MOVLW lower-byte
 MOVWF INDF
Можно конечно это тоже спрятать в подпрограмму, но тогда придётся задействовать один из регистров, для передачи второй половины слова, т.к. одного W будет уже недостаточно...

P.S. Для начала можно лишь 16-ричные числа поддержать (режим HEX), а уже потом сделать десятичные (режим DECIMAL), т.к. для интерпретации десятичных чисел потребуется написать для пика достаточно нетривиальный код...

P.P.S. Форт у нас интерактивный, т.е. он должен позволять ввести интерактивно что-то и сразу же получить результат - в этом случае компиляции в память программ происходить не должно - придётся делать интерпретацию на лету, хотя для начала можно и компилировать - тогда просто пик быстрее из строя выйдет по причине изношенности флеша, но это уже не суть.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

В EEPROM будет храниться указатель на память программ, с которого можно начинать компиляцию вводимых слов.

Если человек вводит : затем имя (скажем XXX) и потом последовательность слов заканчивающихся ; то это компилирует и добавляет в словарь слово XXX.

При вызове команды FORGET XXX будут "забываться" все откомпилированные слова, начиная с XXX, путём простого сдвигания этого самого указателя на начало откомпилированного слова XXX (если слов с именем XXX несколько, то будет браться последняя компиляция слова XXX).

Также можно реализовать поддержку команды MARKER XXX, которая будет добавлять в словарь слово XXX, запустив которое затем можно "забыть" всё, что делалось до него (т.е. аналог FORGET XXX).

Если же человек сразу начинает писать последовательность слов, то после нажатия Enter эта последовательность компилируется и запускается (не сдвигая при этом указатель из EEPROM). Пример:

Code: Select all

2 3 +
5 ok
При этом перед приглашением "ok" Форт печатает состояние стека (обычно включается командой SHOWSTACK и отключается NOSHOWSTACK), которое сохраняется после выполнения команды, т.е.

Code: Select all

5 +
A ok
Как видим прибавив к вершине стека 5 мы получили A (шестнадцатиричное представление числа 10).

P.S. А вообще традиционно состояние стека в Форте печатается командой .S
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Команда VARIABLE XXX добавляет в словарь слово XXX которое кладёт на вершину стека адрес новой переменной (счётчик адресов переменных наверное также придётся хранить в EEPROM, причём при откатывании словаря через FORGET нужно также освобождать память отведённую ранее для освобождаемых переменных).

Команда NNN VALUE XXX добавляет в словарь слово XXX, которое кладёт на вершину стека число NNN (тоже самое будет если слово VALUE заменить на CONSTANT с той лишь разницей, что слово, созданное с помощью CONSTANT не даст себя больше менять, а с VALUE - даст через команду TO).
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Shaos wrote:...остальные же 16-битные числа (-32768...+32767) будут компилироваться в такую вот конструкцию:

Code: Select all

 DECF FSR,F
 MOVLW higher-byte
 MOVWF INDF
 DECF FSR,F
 MOVLW lower-byte
 MOVWF INDF
Можно конечно это тоже спрятать в подпрограмму, но тогда придётся задействовать один из регистров, для передачи второй половины слова, т.к. одного W будет уже недостаточно...
Без подпрограммы, кладущей 16-битное число на стек, по видимому таки не обойтись:

Code: Select all

; put HI and W on stack
PUSH:
 MOVWF LO ; copy W to LO
 DECF FSR,F
 MOVF HI
 MOVWF INDF
 DECF FSR,F
 MOVF LO,0 ; copy LO to W
 MOVWF INDF
 RETURN
Тогда VARIABLE можно будет скомпилировать в

Code: Select all

XXX:
 MOVLW high-byte-adr
 MOVWF HI
 MOVLW low-byte-adr
 CALL PUSH
 RETURN
А вот CONSTANT/VALUE, чтобы отличить его от VARIABLE (а также для скорости) можно делать напрямую без вызова PUSH:

Code: Select all

XXX:
 DECF FSR,F
 MOVLW higher-byte
 MOVWF INDF
 DECF FSR,F
 MOVLW lower-byte
 MOVWF INDF
 RETURN
Другой вопрос как отличить VALUE от CONSTANT, чтобы в первом случае давало модифицировать higher-byte и lower-byte, а во втором - нет. Например в случае CONSTANT можно перед RETURN поставить лишний NOP, хотя возможно в первой версии будет только VALUE...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Потом можно сделать, что если компилятор встречает слово, начинающееся с $, то он будет просто вставлять на этом месте инструкцию, шестнадцатиричный код которой будет идти непосредственно следом за $ в виде 4 шестнадцатиричных цифр, например $0170 при компиляции будет воспринят как пиковская команда BCF 070h,0 (очистить бит 0 регистра 0x70) - понятное дело таким хаком надо пользоватся с большой осторожностью (самое прикольное, что это в интерактивном режиме будет тоже работать ; )

P.S. 11 декабря решил, что $ будет логичнее нежели #
Last edited by Shaos on 11 Dec 2014 00:41, edited 1 time in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Собственно задача состоит в том, чтобы понять насколько продвинутый Форт можно затолкать в пик с 2К...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

DO ... LOOP как известно могут быть вложены - в этом случае внутренний цикл может взять значение счётчика через слово I, а J - это счётчик внешнего цикла. Надо как-то придумать имплементацию - DO должен где-то запоминать адрес возврата (причём не на стеке данных).

IF ... THEN и IF ... ELSE ... THEN при компиляции должны знать куда передавать управление в том или ином случае в момент IF - а при первом проходе это невозможно, т.к. такие конструкции придётся компилировать как минимум в 2 прохода - при первом IF ставится передача управления скажем на нулевой адрес, а уже при полной компиляции строки, когда все адреса известны, нам надо пройти сначала ещё раз, заменяя все нулевые переходы на нужные...
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Ну что - нету у нас тут любителей Форта? Никто мне не скажет, что я просто должен взять книжку и по ней тупо повторить каноническое описание DO и IF с исользованием R>, GOES> и т.д.? ;)
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Lavr
Supreme God
Posts: 16689
Joined: 21 Oct 2009 08:08
Location: Россия

Post by Lavr »

Shaos wrote:Ну что - нету у нас тут любителей Форта?
Да я его просто ненавижу. Сколько не брался - он мне напоминает об убогих стеково-ориентированных программируемых калькуляторах.

Хотя я и попал на этот форум из-за Шурика и его Форта...
Конечно же из-за Шурика и его эпопеи со "Специалистом", но в тот день я искал простые реализации языка Форт.

Бывают у меня такие потуги... найду... почитаю... но резюме пока одинаковое: этот Форт - кал галимый! :-?
(сугубо моё личное мнение)
iLavr
esl
Writer
Posts: 24
Joined: 23 May 2006 13:40

Post by esl »

Lavr wrote:Бывают у меня такие потуги... найду... почитаю... но резюме пока одинаковое: этот Форт - кал галимый! :-?
(сугубо моё личное мнение)
ниче вы не понимаете в теплом ламповом форте ;)

а почти по теме, вспомнил тут форт и это круто!

для Корвета ваяем модуль работы с SD
(а точнее чтоб на любом корвете, даже без дисковода можно подключить к разъему расширения и работать с образами дисков как с настоящими дисками)
собственно оно уже работает ;)

так надо писать утиль MOUNT, писать ее на асме - та ну нафиг
и так на асме уже куча кода.

соавтор проекта - железячный маг с ником forth32 ;) написал ее на C
это скажу вам тот еще гиморой, ибо на 8080 есть только нативные асмы.

а я пробую тут написать на форте,
так скажу я вам - мне это доставляет прям удовольствие
а писать на K&R C - еще тот мазахизм.
esl
Writer
Posts: 24
Joined: 23 May 2006 13:40

Post by esl »

Lavr wrote:Хотя я и попал на этот форум из-за Шурика и его Форта...
Конечно же из-за Шурика и его эпопеи со "Специалистом", но в тот день я искал простые реализации языка Форт.
а можно про "эпопею" подробнее почитать ?
User avatar
Shaos
Admin
Posts: 24078
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

Shaos wrote:Ну что - нету у нас тут любителей Форта? Никто мне не скажет, что я просто должен взять книжку и по ней тупо повторить каноническое описание DO и IF с исользованием R>, GOES> и т.д.? ;)
Вот чего нашёл:

http://stackoverflow.com/questions/6949 ... itten-in-c

Советуют завести специальный стек для циклов - вполне разумно выглядит...

P.S. да я не спорю, что форт странен, но простота реализации компилятора таки завораживает :)
Я тут за главного - если что шлите мыло на me собака shaos точка net
User avatar
Lavr
Supreme God
Posts: 16689
Joined: 21 Oct 2009 08:08
Location: Россия

Post by Lavr »

esl wrote:ниче вы не понимаете в теплом ламповом форте ;)
...
а можно про "эпопею" подробнее почитать ?
Ну раз Вы такой в тёпловом ламповом экскременте понимающий -
Гугыль Вам в руки на фразу:"Специалист" в собственном соку

А я, как ниче не понимающий, большего подмочь не смогу... :lol:
iLavr