Load from the terminal input buffer to parameter s

2019-07-23 08:55发布

问题:

Why does this code not work?

TIB 10 ACCEPT
TIB SP@ 1 cells - 10 cmove

In that code I tried to enter a string and store it in the terminal input buffer and later store it on the parameter stack.

But with .S I see that does not work.

回答1:

The parameter stack grows towards low memory

The main problem with the sample code is that the parameter stack grows towards low memory. So the starting point for the destination of the copy should be at a higher memory address (inside the existing/defined parameter stack). So instead of

TIB SP@ 1 cells - 10 cmove

it should be:

TIB SP@ 1 cells + 10 cmove

Memory allocation for the string

The next problem is that there is not enough storage for the string on the parameter stack. ACCEPT has left over one cell (four bytes on a 32-bit system), the actual number of characters. With sample input "user10181" (9 characters),

TIB 10 ACCEPT

results in:

.S <1> 9  ok

Forgetting about that extra element for the moment1, for the purpose of this elaboration, we allocate four cells on the parameter stack (the actual value, for example, 235, does not matter), 16 bytes on a 32-bit system:

235 DUP DUP DUP

The result of TIB SP@ 1 cells + 10 cmove is then:

.S <5> 9 235 8241 541085779 541215060  ok

We see that three of the four cells (each with four bytes) have been overwritten by cmove (as expected).

TIB is overwritten by subsequent input from the terminal

Unfortunately, our copied bytes are not as expected. Decoding the output for the three changed cells (that are in decimal), first from

8241 541085779 541215060

to hexadecimal:

2031 20405053 20424954

And then decoding as ASCII:

20 31 20 40 50 53 20 42 49 54
   1      @  P  S     B  I  T

And reversing (we had the high memory first and the test platform is little endian):

 "TIB SP@ 1 "

That is the first ten characters of our second line, TIB SP@ 1 cells + 10 cmove. Thus it is clear that the terminal input buffer (TIB) is too temporary to be used in this case.

The solution

The solution to the third problem is to have all the code compiled before we ask for user input. For instance, put it into a word, inputOnStack:

: inputOnStack TIB 10 ACCEPT 235 DUP DUP DUP TIB SP@ 1 cells + 10 cmove ;

The result is then:

inputOnStack user10181  ok
.S <5> 9 235 24881 942747697 1919251317  ok

That corresponds to "user10181" and the tenth character is "a" (most likely from the "a" in inputOnStack).

Test platform:

  • Raspberry Pi, model B.
  • Operating system: Raspbian, as installed by NOOBS 1.3.10, released 2014-09-09.
  • Gforth: version 0.7.0 (installed with sudo apt-get update; sudo apt-get install gforth)

1. A more advanced version of the code could use the actual number of characters. In any case, it should DROPped one way or the other to balance the stack if this code is be integrated into other code.



回答2:

Consider very carefully what happens to the stack after each word. I have reproduced your code below, and annotated it with the stack depth at every point.

( 0 ) TIB ( 1 ) 10 ( 2 ) ACCEPT ( 1 )
( 1 ) TIB ( 2 ) SP@ ( 3 ) 1 ( 4 ) cells ( 4 ) - ( 3 ) 10 ( 4 ) cmove ( 1 )

So when SP@ is executed, it returns a pointer to stack element 2. The pointer is then decremented by one cell, resulting in a pointer to stack element 3 (because the stack grows downwards). cmove then overwrites 10 bytes, i.e. 2 stack elements (guessing you're running a 64-bit forth). So stack elements 3 and 2 are changed. Finally, cmove pops three elements from the stack, leaving only one. Which is unchanged.



回答3:

MU!

Basically what you do here is a copy CMOVE to the area at the stack, on a Forth where SP@ is defined (it is not a standard word). So you destroy the stack. The answer is: such code is not supposed to work.

The question should not be " why doesn't this work?" but "under what circumstance has this the intended effect?"
Let us assume:

  • this Forth has an addressable and writable stack (if you think that's the same read up about Intel segment descriptors and the particulars of POP and MOV instructions)

  • SP@ points to the top of the stack at the moment is called.

  • the stack grows up. (if it grows down the situation is totally different)

  • at the start of the call the stack was empty.

The TIB and ACCEPT are red herrings and will be ignored.

 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 

Assuming a protected Forth, the last bytes are beyond the stack, and lead to a segmentation fault.

So you must readjust your thinking to low level if you want to use Forth. Think of memory as stack of letterboxes and start from there.



标签: forth gforth