Загрузка из входного буфера терминала в стек параметров
Почему этот код не работает?
TIB 10 ACCEPT
TIB SP@ 1 cells - 10 cmove
В этом коде я попытался ввести строку и сохранить ее в буфере ввода терминала, а затем сохранить в стеке параметров.
Но с.S I вижу, что не работает.
3 ответа
Стек параметров растет в сторону нехватки памяти
Основная проблема с примером кода состоит в том, что стек параметров растет в направлении нехватки памяти. Таким образом, отправная точка для места назначения копии должна находиться по более высокому адресу памяти (внутри существующего / определенного стека параметров). Так что вместо
TIB SP@ 1 cells - 10 cmove
так должно быть:
TIB SP@ 1 cells + 10 cmove
Выделение памяти для строки
Следующая проблема заключается в том, что недостаточно места для строки в стеке параметров. ПРИНЯТЬ оставил на одну ячейку (четыре байта в 32-битной системе) фактическое количество символов. С примером ввода "user10181" (9 символов),
TIB 10 ACCEPT
результаты в:
.S <1> 9 ok
Забыв об этом дополнительном элементе на момент 1, для целей данной разработки мы выделяем четыре ячейки в стеке параметров (фактическое значение, например, 235, не имеет значения), 16 байтов в 32-разрядной системе:
235 DUP DUP DUP
Результат TIB SP@ 1 cells + 10 cmove
затем:
.S <5> 9 235 8241 541085779 541215060 ok
Мы видим, что три из четырех ячеек (каждая с четырьмя байтами) были перезаписаны cmove
(как и ожидалось).
TIB перезаписывается последующим вводом с терминала
К сожалению, наши скопированные байты не так, как ожидалось. Декодирование выходных данных для трех измененных ячеек (в десятичной форме), сначала из
8241 541085779 541215060
в шестнадцатеричное:
2031 20405053 20424954
А затем расшифровка как ASCII:
20 31 20 40 50 53 20 42 49 54
1 @ P S B I T
И в обратном порядке (сначала у нас было много памяти, а тестовая платформа была с прямым порядком байтов):
"TIB SP@ 1 "
Это первые десять символов нашей второй строки, TIB SP@ 1 cells + 10 cmove
, Таким образом, ясно, что входной буфер терминала (TIB) является слишком временным для использования в этом случае.
Решение
Решение третьей проблемы состоит в том, чтобы весь код был скомпилирован до того, как мы запросим ввод данных пользователем. Например, поместите это в слово, inputOnStack
:
: inputOnStack TIB 10 ACCEPT 235 DUP DUP DUP TIB SP@ 1 cells + 10 cmove ;
Результат тогда:
inputOnStack user10181 ok
.S <5> 9 235 24881 942747697 1919251317 ok
Это соответствует "user10181", а десятый символ - "a" (скорее всего, из "a" в inputOnStack
).
Тестовая платформа:
- Raspberry Pi, модель B.
- Операционная система: Raspbian, установленная NOOBS 1.3.10, выпущена 2014-09-09.
- Gforth: версия 0.7.0 (устанавливается с
sudo apt-get update; sudo apt-get install gforth
)
1. Более продвинутая версия кода может использовать фактическое количество символов. В любом случае, он должен DROPped так или иначе сбалансировать стек, если этот код интегрирован в другой код.
Очень внимательно рассмотрим, что происходит со стеком после каждого слова. Я воспроизвел ваш код ниже и аннотировал его глубиной стека в каждой точке.
( 0 ) TIB ( 1 ) 10 ( 2 ) ACCEPT ( 1 )
( 1 ) TIB ( 2 ) SP@ ( 3 ) 1 ( 4 ) cells ( 4 ) - ( 3 ) 10 ( 4 ) cmove ( 1 )
Так когда SP@
выполняется, он возвращает указатель на элемент стека 2. Затем указатель уменьшается на одну ячейку, что приводит к указателю на элемент стека 3 (поскольку стек растет вниз). cmove
затем перезаписывает 10 байтов, то есть 2 элемента стека (предполагая, что вы запускаете 64-разрядный код вперед). Таким образом, элементы стека 3 и 2 изменены. В заключение, cmove
выталкивает три элемента из стека, оставляя только один. Который без изменений.
MU!
По сути, здесь вы копируете CMOVE в область стека на Forth, где определен SP@ (это не стандартное слово). Таким образом, вы уничтожаете стек. Ответ: такой код не должен работать.
Вопрос не должен быть "почему это не работает?" но "при каких обстоятельствах это имеет ожидаемый эффект?"
Давайте предположим:
этот Forth имеет адресуемый и записываемый стек (если вы думаете, что это то же самое, что читают о дескрипторах сегментов Intel и подробностях инструкций POP и MOV)
SP@ указывает на вершину стека, в данный момент вызывается.
стек растет. (если он растет, ситуация совершенно иная)
в начале вызова стек был пуст.
TIB и ACCEPT являются красными сельдями и будут игнорироваться.
1 cells - \ The pointer to the stack is changed opening up one cell
100 cmove \ you overwrite one cell below the stack,
\ then the (32-bit) cell place where the result of SP@ resides *and*
\ then TIB and then a couple of more bytes
При условии защищенного Forth последние байты находятся за пределами стека и приводят к ошибке сегментации.
Поэтому вы должны перенастроить свое мышление на низкий уровень, если хотите использовать Forth. Думайте о памяти как о стеке почтовых ящиков и начинайте оттуда.