Я написал интерпретатор Forth для процессора J1, который сейчас портирую на Z80. В новой версии слово через двоеточие представляет собой список адресов, по которым будет обращаться интерпретатор.
Моя проблема заключается в том, как вставить литералы в определение двоеточия.
Пример:
: 1+ 1 + ;
Когда компилятор встречает литерал 1, у него нет адреса для вызова, который помещает значение в стек.
Решением было бы динамически генерировать такую процедуру, как
ld hl, 1
push hl
ret
где-нибудь еще и используйте этот адрес в определении через двоеточие.
Другое решение — использовать недопустимый адрес (например, 0x0000), за которым следует литерал, что интерпретатор будет рассматривать как особый случай: при обнаружении адреса 0x0000 он просто помещает значение следующих двух байтов в стек. В этом случае определение 1+ в памяти будет таким:
0x0000 ;
0x0001 ; literal
0x4500 ; + routine address
Есть ли лучшее решение?
Редактировать
В машинном коде J1 1+ компилируется в это
addr code comments
0125 8001 1 ; Bit 15=1 push literal into stack
0126 628f %+ ; This combine op codes PLUS and RET
J1 — это машина на Форте, поэтому слова переводятся непосредственно в машинный код. Мой интерпретатор компилирует машинный код, где значения автоматически помещаются в стек. См. thh.org/svfig/kk/11-2010-Bowman.pdf
Классический подход — манипулировать обратным адресом:
: (lit) ( -- x ) ( R: addr1 -- addr2 )
\ NB: "(lit)" should not be used as an ordinary word
r> dup cell+ >r @
;
: lit, ( x -- )
['] (lit) compile, ,
\ it is assumed that code space
\ is united with data space
;
: literal ( x -- x | )
state @ if lit, then
; immediate
Слово (lit)
выше также известно как простое lit
. Традиционно в Форте имя слова в скобках используется, чтобы подчеркнуть, что это слово предназначено только для внутренних целей.
Давайте рассмотрим определение foo
:
: foo 42 + ;
Пусть одна ячейка занимает две единицы адреса, а тело foo
начинается с адреса 1024. Тогда потоковый код тела состоит из следующих элементов:
(lit)
42
+
exit
Когда (lit)
вызывается из foo
, верхнее значение в стеке возврата равно 1026. Это адрес, по которому хранится число 42
. (lit)
извлекает верхнее значение из стека возврата (1026), увеличивает его копию на размер ячейки и помещает результат (1028) в стек возврата. Он также извлекает значение по адресу 1026, оставляя в стеке 42. Затем выполнение продолжается на этапе 1028.
Значение, которое следует за инструкцией в двоичном коде и от которого зависит поведение инструкции, иногда называется «непосредственным аргументом» (непосредственным аргументом инструкции). Инструкцию с ее непосредственным аргументом можно рассматривать как одну инструкцию большего размера.
Не только (lit)
имеет непосредственный аргумент. Инструкции условного перехода и перехода обычно также имеют непосредственный аргумент.
Альтернативный подход — генерировать анонимный примитив для каждой уникальной пары инструкции и ее непосредственного аргумента. Я не уверен, что этот подход использовался на практике.
См. также: Открытый интерпретатор: переносимость стека возврата Манипуляции , М.Л.Гасаненко, 1998г.
Я не понимаю, как это работает. Я уже компилирую слово, когда найден новый литерал (например, 42). Можете ли вы расширить?
@CandidMoe, см.: Открытый интерпретатор: переносимость манипуляций со стеком возврата. Дайте мне знать, если что-то неясно.
Похоже, мне придется переписать 1+ как : 1+ lit [ 1 , ] + ;
, что некрасиво. А еще у меня есть xt
из lit
; просто я не понимаю, где хранить конкретное значение с помощью DTC. Есть только на lit
, но значений много.
Почему бы и нет : 1+ [ 1 lit, ] + ;
? Но это не обязательно. Обычно это делается текстовым интерпретатором Форта (или целевым компилятором). Я добавил конкретный пример того, как это работает.
Да, я думал в том же направлении. Это умаляет чистоту реализации «списка адресов», но работает.
По поводу чистоты списка адресов — даже без литералов он не такой чистый: условные переходы и переходы обычно тоже имеют непосредственный аргумент (пост обновил).
Поскольку эта проблема не зависит от целевого процессора: как вы решили ее на своем бывшем Форте? -- Боюсь, что ваш вопрос приведет к самоуверенным ответам и на этом можно закрыться.