0004: целевой компилятор
Этот выпуск рассылки пока будет последним в серии о tinyVM -- следующие
выпуски по этой теме будут по вашим письмам и по графической библиотеке для
tinyVM, которой я сейчас занимаюсь.
Целевой комилятор (ЦК) -- расширение форт-системы, превращающее ее в
ассемблер и кросс-компилятор.
Основной принцип -- создается буфер
0x10000 CONSTANT Msz \ максимальный размер программы
CREATE M Msz CELLS ALLOT \ буфер
0 VALUE THERE \ указатель компиляции (на свободный элемент)
Слово
: CELLS ( n -- n*cell) CELL * ;
берет со стека число элементов и умножает на размер элемента в байтах.
В таком виде и при использовании 32-битной форт-системы типа
SP-FORTH http://spf.sf.net будет генерироваться 32-битный байт-код.
Если вы хотите переключить ЦК в 16-битный режим, перед загрузкой ЦК или
перед переопределением CELLS нужно задать строку
2 CONSTANT CELL
Для сохранения байт-кода в файл определяем слово
: save" \ program"
\ сохранить байт-код в файл program и выйти из форт-системы
[CHAR] " WORD COUNT W/O CREATE-FILE DROP ( fh )
M THERE CELLS ROT ( M THERE*CELL fh ) WRITE-FILE DROP BYE
;
Для чтения и записи чисел в буфер ЦК определяем слова
: cell! ( n addr -- ) CELLS M + CELL 2 = IF W! ELSE ! THEN ;
: cell@ ( addr -- n ) CELLS M + CELL 2 = IF W@ ELSE @ THEN ;
которые записывают и читают n по смещению addr от начала M. При этом
проверяется значение CELL и выбирается соответствующий вариант чтения/записи
памяти форт-системы (чтение/запись 32- или 16-битного слова).
Осталось определить слово
: cell, ( n -- ) THERE cell! THERE 1+ TO THERE ;
компилирующее n в конец уже скомпилированного байт-кода.
В таком варианте уже можно использовать ЦК:
test.4th
=================================
\ 2 CONSTANT CELL \ раскомментировать для генерации 16-битного байт-кода
S" tc.spf" INCLUDED
0x1234 cell, 0x5678 cell,
save" test.bc"
=================================
и должен получиться файл test.bc
=================================
34 12 00 00 78 56 00 00
=================================
Теперь нам нужно определить набор слов, компилирущих управляющие конструкции.
Для этого сначала нужно определеить несколько команд.
Сначала используя CREATE DOES> определим слова
: 0op CREATE , DOES> @ cell, ; \ команда
: 1op ( n ) CREATE , DOES> @ cell, cell, ; \ команда
затем определяем несколько команд (описание см. выпуск [0003])
0x00 0op nop
0x01 1op jmp 0x02 1op ?jmp 0x03 1op call 0x04 0op ret
0x05 1op lit
0x06 0op exec
0x08 0op do 0x09 0op loop
0x0A 0op i 0x0B 0op j 0x0C 0op k
и управляющие структуры (подробное описание по запросу)
1 CONSTANT _entry \ см. стартовый код далее
: { CREATE THERE , THERE _entry cell! DOES> @ call ;
: } ret ;
: const CREATE ( n ) , DOES> @ lit ;
: var CREATE THERE , ( n ) cell, DOES> @ lit ;
: begin THERE ;
: again jmp ;
: until ?jmp ;
: while -1 ?jmp THERE 1- ;
: repeat SWAP jmp THERE SWAP cell! ;
: if -1 ?jmp THERE 1- ;
: then THERE SWAP cell! ;
: else -1 jmp THERE 1- SWAP then ;
остальные команды (полную версию см. http://akps.ssau.ru/forth/tiny/tc.spf)
0x10 0op dup 0x11 0op drop 0x12 0op swap 0x13 0op over
0x20 0op fetch 0x21 0op store
0x30 0op add 0x31 0op sub 0x32 0op mul 0x33 0op div
0x40 0op less 0x41 0op great 0x42 0op eq 0x43 0op noteq
0x60 0op key 0x61 0op emit 0x62 0op print 0x63 0op dot
0x70 0op sdot
0x7F 0op bye
Слово { при своем выполнении переопределяет точку запуска программы на
последнее определенное через { } слово, модифицируя стартовый код:
\ start-up код
-1 jmp \ jmp entry
Константа _entry задает адрес параметра команды jmp
Все, это весь компилятор (ассемблер) для tinyVM 8-)
Основную сложность в понимании того, как работает этот ЦК, имеют слова
управляющих конструкций -- советую переписать их самостоятельно, чтобы понять
как они работают.
код1 begin код2 again код3
должен компилироваться в
код1
begin-addr:
код2
jmp begin-addr
код3
(для сохранения адресов типа begin-addr используйте стек)
код1 begin код2 until код3
код1
begin-addr:
код2
?jmp begin-addr
код3
код1 if код2 else код2 then код4
код1
if-addr:
?jmp else-addr
код 2
jmp then-addr
else-addr:
код3
then-addr:
код4
(компилировать ?jmp и jmp с фиктивным адресом типа -1, запомнив адрес
параметра команд (?)jmp на стеке, а в словах else и then использовать
сохраненные адреса параметров для их изменения на реальные адреса)
код1 begin код2 while код3 repeat код4
код1
begin-addr:
код2
?jmp repeat-addr
код3
jmp begin-addr
repeat-addr:
код4
{ someword код }
{ создает в словаре форт-системы слово
: someword <значение THERE при исполнении {> call ;
которое при своем выполнении компилирует команду call
Фактически { запоминает текущую позицию компиляции, создав слово, которое
скомпилирует CALL на эту позицию.
К сожалению написать create does> для целевого компилятора мне не удалось 8-(,
но пока в этом нет необходимости.
При использовании ЦК можно пользоваться двумя вариантами определения новых
слов:
: macroword код1 ;
{ tcword macroword код2 macroword }
Слово macroword при этом определяется как макрос, и компилируется в целевой
код inline, а слово tcword компилируется как процедура:
tcword:
код1
код2
код1
ret
Теперь если вы хотите полностью понять, как это работает, и научиться
пользоваться tiny, советую скачать SP-FORTH или другую форт-систему
соответствующую стандарту ANS FORTH '94, полную версию движка, целевого
компилятора и примеров с http://akps.ssau.ru/forth/tiny/ и полностью
переписать движок (лучше на любом другом языке или ассемблере) и ЦК так, чтобы
примеры работали.
Если возникают какие-то вопросы, или что-то не получается, обязательно
свяжитесь со мной, и в следующих выпусках рассылки ваши вопросы будут
расжеваны более подробно и с примерами.
PS: для работы ЦК необходима регистро-зависимая форт-система,
считающая слова SOMEWORD и someword разными словами
================
http://akps.ssau.ruforth@km.ru
FidoNet SU.FORTH 2:5057/18.29
tel.: +7 8462 28 9910 (work), 15 4313 (home)