10.11Saving temporary values. 4tH 4tH Forth compiler
বিজ্ঞাপন
বিজ্ঞাপন
CHAPTER 10. INTEGER SEGMENT AND CODE SEGMENT
10.11
Saving temporary values
125
We haven’t shown you how the Return Stack works just for the fun of it. Although it is an area that is almost exclusively used by the system you can use it too.
We know we can manipulate the Data Stack only three items deep (using ’ROT’). Most of the time that is more than enough, but sometimes it isn’t.
In 4tH there are special words to manipulate stack items in pairs, e.g. ’2DUP’ ( n1 n2 – n1 n2 n1 n2) or ’2DROP’ ( n1 n2 –). Although they are already part of 4tH, we could easily define those two ourselves:
: 2dup over over ;
: 2drop drop drop ;
You will notice that ’2SWAP’ ( n1 n2 n3 n4 – n3 n4 n1 n2) becomes a lot harder. How can we get this deep? You can use the Return Stack for that..
The word ’>R’ takes an item from the Data Stack and puts it on the Return Stack. The word ’R>’ does it the other way around. It takes the topmost item from the Return Stack and puts it on the Data Stack. Let’s try it out:
: 2swap ( n1 n2 n3 n4) rot
>r rot r>
( n1 n3 n4 n2)
( n1 n3 n4)
( n3 n4 n1)
( n3 n4 n1 n2)
;
\ four items on the stack
\ rotate the topmost three
\ n2 is now on the Return Stack
\ rotate other items
\ get n2 from the Return Stack
And why does it work in this colon-definition? Why doesn’t the program go haywire?
Because the Return Stack is and was perfectly balanced. The only thing we had to do was to get off "n2" before the semi-colon was encountered. Remember, the semi-colon compiles into ’EXIT’ and ’EXIT’ pops a return-address from the Return Stack. Okay, let me show you the Return Stack effects:
: 2swap ( r1) rot ( r1)
>r rot r>
( r1 n2)
( r1 n2)
( r1)
; ( --)
Note, these are the Return Stack effects! "R1" is the return-address. And it is there on top on the Return Stack when ’EXIT’ is encountered. The general rule is:
"Clean up your mess inside a colon-definition"
If you save two values on the Return Stack, get them off there before you attempt to leave.
If you save three, get three off. And so on. This means you have to be very careful with looping and branching. Otherwise you have a program that works perfectly in one situation and not in another:
: this-wont-work ( n1 n2 -- n1 n2)
>r ( n1)
0= if r> dup else
1 2
( --)
( n2)
( n2 n2)
( 1 2) then
;
CHAPTER 10. INTEGER SEGMENT AND CODE SEGMENT 126
This program will work perfectly if n1 equals zero. Why? Let’s look at the Return Stack effects:
: this-wont-work ( r1)
>r
0= if r> dup else
1 2 then
( r1 n2)
( r1 n2)
( r1)
( r1)
( r1 n2)
( r1 n2)
;
You see when it enters the ’ELSE’ clause the Return Stack is never cleaned up, so 4tH attempts to return to the wrong address. Avoid this, since this can be very hard bugs to fix.
10.12
The Return Stack and the DO..LOOP
We’ve already told you that the limit and the counter of a DO..LOOP (or DO..+LOOP) are stored on the Return Stack. But how does this affect saving values in the middle of a loop?
Well, this example will make that quite clear:
1 ( n)
10 0 do ( n)
>r i .
r>
( --)
( --)
( n) loop cr drop
( n)
( n)
( --)
You might expect that it will show you the value of the counter ten times. In fact, it doesn’t.
Let’s take a look at the Return Stack:
1 ( --)
10 0 do ( l c)
>r i .
( l c n)
( l c n) r> loop cr drop
( l c)
( --)
( --)
( --)
You might have noticed that it prints ten times the number "1". Where does it come from?
Usually ’I’ prints the value of the counter, which is on top of the Return Stack.
This time it isn’t: the number "1" is there. So ’I’ thinks that "1" is actually the counter and displays it. Since that value is removed from the Return Stack when ’LOOP’ is encountered, it doesn’t do much harm.
We see that we can safely store temporary values on the Return Stack inside a DO..LOOP, but we have to clean up the mess, before we encounter ’LOOP’. So, this rule applies here too:
"Clean up your mess inside a DO..LOOP"
ডাউনলোড
বিজ্ঞাপন