Загрузка из входного буфера терминала в стек параметров

Почему этот код не работает?

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. Думайте о памяти как о стеке почтовых ящиков и начинайте оттуда.

Другие вопросы по тегам