+Vorticon Posted October 28, 2012 Share Posted October 28, 2012 (edited) Thanks! This definitely does the trick. Now another question: How does one make the address of a variable be the same as the address of another? What I'm trying to do is create a pointer to a matrix in a definition, and the only thing I could come up with was something like this: 0 VARIABLE MATRIX 16 ALLOT 0 VARIABLE POINTER : TEST POINTER ! POINTER @ 18 + POINTER @ DO I @ . 2 +LOOP ; then I would call TEST as such MATRIX TEST and get a listing of the matrix' contents. TEST obviously could be reused for any 18 cell matrix as long as the matrix address is passed on to it. There has go to be a less clumsy way to do this. Edited October 28, 2012 by Vorticon Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 Yes. Have a look at my TF code further up this thread. Notice that the offsets are different for the assembly definition and Forth equivalent? That's because if you implement it in Forth, then the act of calling that word in your subsequent loop invokes a return stack push (the call to DOCOL) so that EXIT can return to where it was in your loop. Thus, everything has "moved down" by one stack position if using a colon definition. Should be the same in TIF I reckon as both systems are pretty much 'classical' Indirect Threaded implementations. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 Thanks! This definitely does the trick. Now another question: How does one make the address of a variable be the same as the address of another? What I'm trying to do is create a pointer to a matrix in a definition, and the only thing I could come up with was something like this: 0 VARIABLE MATRIX 16 ALLOT 0 VARIABLE POINTER : TEST POINTER ! POINTER @ 18 + POINTER @ DO I @ . 2 +LOOP ; then I would call TEST as such MATRIX TEST and get a listing of the matrix' contents. TEST obviously could be reused for any 18 cell matrix as long as the matrix address is passed on to it. There has go to be a less clumsy way to do this. Yes. You don't need the variable at all. The stack is your friend. Only resort to variables when not doing so would result in 'stackrobatics' (i.e excessive and ugly (and time consuming) stack juggling to get you stack in the order you need it). In this case, a variable is going over the top a bit. We just need the start address of the array and we can work everything else out using that: 0 VARIABLE MATRIX 16 ALLOT : TEST ( addr -- ) DUP 18 + SWAP DO I @ . 2 +LOOP ; Ta dah! Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 Here's another way (I would argue a better way) to skin the cat. If TIF doesn't have CELLS then define it as follows: : CELLS ( #cells -- #bytes) 2* ; 0 VARIABLE MATRIX 8 CELLS ALLOT : .MATRIX ( addr -- ) 9 0 DO DUP I CELLS + @ . LOOP DROP ; Here I've called the word .MATRIX. In Forth, as you know, . (dot) is associated with displaying numeric values. Therefore we use the dot in the name. Much better than the extremely generic and oft over-used TEST Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted October 28, 2012 Share Posted October 28, 2012 Yes. You don't need the variable at all. The stack is your friend. Only resort to variables when not doing so would result in 'stackrobatics' (i.e excessive and ugly (and time consuming) stack juggling to get you stack in the order you need it). In this case, a variable is going over the top a bit. We just need the start address of the array and we can work everything else out using that: 0 VARIABLE MATRIX 16 ALLOT : TEST ( addr -- ) DUP 18 + SWAP DO I @ . 2 +LOOP ; Ta dah! Thanks Willsy, but I think I should clarify as I probably used too simple an example. I am aware of the above, but my real world needs involve multiple arrays being passed to a function without necessarily being part of a DO loop, Which is why I am looking for a way assign the address of a variable to that of matrix. In other words, using my example above, POINTER would leave the address of the matrix on the stack instead of using POINTER ! and POINTER @ to do the same... Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 Perhaps you want an array of matrix addresses? (Not tested in TIF) 0 VARIABLE MATRIX0 16 ALLOT 0 VARIABLE MATRIX1 16 ALLOT 0 VARIABLE MATRIX2 16 ALLOT 0 VARIABLE MATRIX3 16 ALLOT CREATE MATRICES MATRIX0 , MATRIX1 , MATRIX2 , MATRIX3 , : SELECT ( n -- matrix_addr) 2* MATRICES + @ ; : .MATRIX ( matrix_id -- ) SELECT DUP 18 + SWAP DO I @ . 2 +LOOP ; Now you can select the address of a matrix by passing a value to SELECT. SELECT gives you the address of the matrix. So, if you want matrix 0: 0 SELECT Leaves the address of matrix 0 on the stack. 1 SELECT Leaves the address of matrix 1 on the stack. .MATRIX above uses select. So, if you want to see matrix 3: 3 .MATRIX Note that we still haven't used a variable as a pointer. SELECT gives you your pointer. Is that kind of what you were getting at? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 Yes, you're right, it is the 5th value on the stack. I don't recall seeing I' mentioned in the TIF manual. I might be able to come up with a workaround to avoid having 3 nested loops, but it won't be as neat. Alternatively, I could create a word like this: : INDEX3 R> R> R> R> R> J' ! >R >R >R >R >R ; where J' is defined as a simple variable. I'm not sure what kind of performance hit using this scheme repeatedly will result in, but it's worth experimenting with. It's becoming clear to me that I will have to eventually learn to use the TIF assembler at some point... A few things: You used up one of the values taken from the return stack with R> J' ! , so your code will cause a stack underflow and corrupt the return stack. J' as a variable will likely not be up to date when you check it. It is better to define it as a word that gets the current value every time it is executed. To keep the loop metaphor consistent, you should use a different letter for the third loop, something like K . J' implies the 2nd loop's limit, not the 3rd loop's index. You will want to leave a value on the stack when you execute INDEX3 or K if you define it as suggested in (2). Perhaps (The following code does not work and will also corrupt the return stack! This is explained in later posts.) : INDEX3 R> R> R> R> R >R >R >R >R ; or : K R> R> R> R> R >R >R >R >R ; Also, I' is not defined in TI Forth, it is mentioned (and defined) in "Appendix C" of the TI Forth Instruction Manual because the first edition of Brodie's Starting Forth explains it on pages 110 and 123. ...lee I feel compelled to re-visit the above because I will have misled anyone who read it. @Vorticon, your suggested code will work with the addition of R to read the 6th item down, necessary due to DOCOL's use of the return stack as @Willsy explained; but, as I indicated, I would use K , not J' : : INDEX3 R> R> R> R> R> R K ! >R >R >R >R >R ; It is far messier to rescue my suggested code above: :K R> R> R> R> R> R SWAP >R SWAP >R SWAP >R SWAP >R SWAP >R ; As you can see from a few earlier posts, the TIF assembler solution is probably the best, with the use of the TIF return stack pointer's address (831Ch) in a high-level definition as a not too distant second. Sorry for the confusion. ...lee Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 I tried CREATE in TIF but doesn't seem to do what I thought it would do. It doesn't give an error, but it doesn't seem to create a word either...? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 I tried CREATE in TIF but doesn't seem to do what I thought it would do. It doesn't give an error, but it doesn't seem to create a word either...? CREATE does, in fact, create a word in the CURRENT vocabulary in TIF. The smudge bit (comparable to TF's hidden bit), however, is toggled on to prevent the word's being found. In TIF, CREATE is usually used by other defining words like <BUILDS . If you want to find the word you created with CREATE , you can toggle the smudge bit with SMUDGE (comparable to TF's HIDDEN ). CREATE creates the word's dictionary header with the code field pointing at the word's parameter field (actually HERE after creation) and the smudge bit set. Even without toggling the smudge bit off, you can see that something was added to the dictionary by checking HERE before and after using CREATE . ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 (edited) <duplicate post> Edited November 16, 2012 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted October 28, 2012 Share Posted October 28, 2012 Perhaps you want an array of matrix addresses? (Not tested in TIF) 0 VARIABLE MATRIX0 16 ALLOT 0 VARIABLE MATRIX1 16 ALLOT 0 VARIABLE MATRIX2 16 ALLOT 0 VARIABLE MATRIX3 16 ALLOT CREATE MATRICES MATRIX0 , MATRIX1 , MATRIX2 , MATRIX3 , : SELECT ( n -- matrix_addr) 2* MATRICES + @ ; : .MATRIX ( matrix_id -- ) SELECT DUP 18 + SWAP DO I @ . 2 +LOOP ; Now you can select the address of a matrix by passing a value to SELECT. SELECT gives you the address of the matrix. So, if you want matrix 0: 0 SELECT Leaves the address of matrix 0 on the stack. 1 SELECT Leaves the address of matrix 1 on the stack. .MATRIX above uses select. So, if you want to see matrix 3: 3 .MATRIX Note that we still haven't used a variable as a pointer. SELECT gives you your pointer. Is that kind of what you were getting at? Yes, this is exactly what I'm looking for. Thanks! That said, I do have a couple of questions about this: - Since the matrices were defined prior to create, wouldn't CREATE create different addresses for the items following it? - Why is , needed? I'll let Lee tell us why CREATE is misbehaving in TIF Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted October 28, 2012 Share Posted October 28, 2012 CREATE does, in fact, create a word in the CURRENT vocabulary in TIF. The smudge bit (comparable to TF's hidden bit), however, is toggled on to prevent the word's being found. In TIF, CREATE is usually used by other defining words like <BUILDS . If you want to find the word you created with CREATE , you can toggle the smudge bit with SMUDGE (comparable to TF's HIDDEN ). CREATE creates the word's dictionary header with the code field pointing at the word's parameter field (actually HERE after creation) and the smudge bit set. Even without toggling the smudge bit off, you can see that something was added to the dictionary by checking HERE before and after using CREATE . ...lee OK this is a little beyond my pay grade unfortunately, at least for now Nonetheless, how would Willsy's code be modified to make it work as intended? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 Yes, this is exactly what I'm looking for. Thanks! That said, I do have a couple of questions about this: - Since the matrices were defined prior to create, wouldn't CREATE create different addresses for the items following it? - Why is , needed? I'll let Lee tell us why CREATE is misbehaving in TIF CREATE is not misbehaving in TIF. It is behaving differently as I explained in my last post. TIF, as you will remember, is written to the fig-Forth "standard", which predates even Forth-79. CREATE in TF is Forth-83 compliant. The difference (other than the smudge/hidden bit) is that TIF has the CFA pointing to the word's PFA, which will execute what's in the PFA when the word is executed unless changed by some other defining word that may use CREATE , and TF has the CFA pointing to a routine in TF that will leave the word's PFA on the stack when the word is executed. Clear as mud, right? ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 (edited) OK this is a little beyond my pay grade unfortunately, at least for now Nonetheless, how would Willsy's code be modified to make it work as intended? Simply this in TIF: MATRIX0 VARIABLE MATRICES MATRIX1 , MATRIX2 , MATRIX3 , ...lee Edited October 28, 2012 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 28, 2012 Share Posted October 28, 2012 - Since the matrices were defined prior to create, wouldn't CREATE create different addresses for the items following it? No. Take another look. It's much simpler than you think it is. Like most things in Forth. 0 VARIABLE MATRIX0 16 ALLOT 0 VARIABLE MATRIX1 16 ALLOT 0 VARIABLE MATRIX2 16 ALLOT 0 VARIABLE MATRIX3 16 ALLOT CREATE MATRICES MATRIX0 , MATRIX1 , MATRIX2 , MATRIX3 , The line above two logically distinct sections/phases. Hence I've group them by colour. So, the red part first: CREATE executes. It reads ahead in the input buffer (the stuff you typed in) and creates a dictionary entry. The behaviour of that word in the dictionary (MATRICES) is to return the address of it's body. Note also, that the compile address (called HERE) is also pointing at the body of matrices, but it's empty at the moment. CREATE has no further involvement. It's done. Then MATRIX0 executes. It's a variable, so it returns its address on the stack. Then comma executes. Comma takes what's on the stack, and compiles it to memory (it gets compiled at the address pointed to by HERE) and advances HERE. So, the address of MATRIX0 gets compiled into MATRICES body address. They kind of overlap. I've shown that with an underlined section. Next, the other variables are "comma'd" into memory in the same way. Thus, we have built up an array of addresses all next to each other. Executing MATRICES returns the address of it's BODY, which has the address of MATRIX0 in it. By adding an offset to this body address, we can access any of the addresses in the list. Make sense? - Why is , needed? See above. To see what comma does, start up TF in Classic99 and open classic99's debugger. In the CPU window, set the address to B000 Now, in TF, set HERE (the compilation address) to $B000 with this code: $9000 H ! Now, type the following: $994A , $BEEF , $FACE , See? Now type HERE $. See? Stick around. We'll make a Forth guru of you yet. Part of the complexity of understanding Forth is understanding just how astonishingly simple it is. It takes a while to sink in! Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 28, 2012 Share Posted October 28, 2012 (edited) Yes, this is exactly what I'm looking for. Thanks! That said, I do have a couple of questions about this: - Since the matrices were defined prior to create, wouldn't CREATE create different addresses for the items following it? - Why is , needed? I'll let Lee tell us why CREATE is misbehaving in TIF Sorry, I missed your other questions: A word created in TF with CREATE ( MATRICES here) will establish the word's header in the dictionary and advance HERE to the next dictionary position, which is the address the created word will leave on the stack when it is executed. Words created with VARIABLE leave their parameter field addresses on the stack when they are executed. Comma ( , ) compiles the number on the stack into the dictionary at HERE and advances HERE by one cell. Because the definition of MATRICES consists of its header followed immediately by the addresses, in order, of MATRIX0 , MATRIX1 , MATRIX2 and MATRIX3 , executing MATRICES will push onto the stack the address of its parameter field, which contains the address of MATRIX0 . To get the other addresses, you simply add 2 to that address for each successive address. The address of MATRIX3 , for example, would be obtained by MATRICES 6 + @ <---I really should be more careful! There's nothing stopping you, of course, from getting addresses deeper into MATRICES , but they will point to a part of memory beyond your setup of MATRICES and be meaningless---programmer, beware! ...lee Edited October 29, 2012 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 29, 2012 Share Posted October 29, 2012 Yes, this is exactly what I'm looking for. Thanks! That said, I do have a couple of questions about this: - Since the matrices were defined prior to create, wouldn't CREATE create different addresses for the items following it? - Why is , needed? I'll let Lee tell us why CREATE is misbehaving in TIF I was a little quick on the trigger with Post #241. MATRICES contains a list of addresses, so to get the address of MATRIX3 in MATRICES , you would execute the following: MATRICES 6 + @ I forgot the @ to fetch the address in Post #241. ...lee Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted October 29, 2012 Share Posted October 29, 2012 Thanks guys. Great explanations of what initially appeared to be quire arcane. The problem is that now I have to go back a re-write my code using this technique! Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted November 18, 2012 Share Posted November 18, 2012 (edited) So here's a thornier problem I have been battling with in TI Forth: I'd like to request user terminal input for the name of a previously defined word, and then find a way to execute that word. I can do the first part using EXPECT, storing the name in an array. What I can't figure out is how to get TIF to execute that name... At first I thought I'll place the ASCII codes of the name on the stack and then do an INTERPRET, but that did not work. I'm still not completely clear on what is meant by the input stream. For example, I have the word TEST previously defined in a colon definition. I use EXPECT to request the user to type the name TEST at the terminal, and the name TEST is stored in an array. I can get that far successfully. Now how do I get TEST to execute from there without any further input from the user??? I know it's an odd request, but I assure you there is a perfectly good rationale behind it ( more on this later ) Edited November 18, 2012 by Vorticon Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted November 18, 2012 Share Posted November 18, 2012 OK I'm going to answer my own question After scouring Starting Forth for some solution, it dawned on me that the input stream starts at S0. My error was storing the user input in an array when I could simply do a S0 @ EXPECT and have that input right at the beginning of the input stream. From there, re-initializing IN with 0 IN ! followed by INTERPRET executed the user input. voila! I'll give myself a thumbs up I still think assembly is simpler... Quote Link to comment Share on other sites More sharing options...
Willsy Posted November 18, 2012 Share Posted November 18, 2012 I can't answer for TI Forth, I'm afraid. However, you probably need to look up the words WORD and FIND. In TF you could do it like this: (it is probably very similar in TIF). : RUN-WORD ( -- ) CR ." Enter a word name:" TIB @ 30 EXPECT CR BL WORD FIND 0= IF ." Word not found" DROP ELSE EXECUTE THEN ; Check out the glossary for an explanation of the words. But in the very latest build of TF (which I haven't posted yet I don't think... can't remember) there is a word called EVALUATE. You simply give it a string (i.e. an address and length on the stack) can call EVALUATE and it will execute *whatever* it finds in there. There are no restrictions. For example, you could do: S" : TEST 1 2 3 ;" EVALUATE And you will end up with a word in dictionary called test, just as you would expect! Nice! Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted November 19, 2012 Share Posted November 19, 2012 Nice! I can't wait for the final cartridge release and the manual! Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted November 19, 2012 Share Posted November 19, 2012 OK I'm going to answer my own question After scouring Starting Forth for some solution, it dawned on me that the input stream starts at S0. My error was storing the user input in an array when I could simply do a S0 @ EXPECT and have that input right at the beginning of the input stream. From there, re-initializing IN with 0 IN ! followed by INTERPRET executed the user input. voila! I'll give myself a thumbs up I still think assembly is simpler... In TI Forth, it is the terminal input buffer (address always in TIB ) that starts at the address in S0 , not necessarily the input stream. The input stream can come from a block, as well. The system knows which by the value in BLK . Only if BLK contains 0, is the terminal input buffer the input stream. S0 is also the address where the parameter stack starts and grows down in memory away from the TIB. TIB and S0 happen to contain the same address at system startup; but, that situation doesn't have to persist. You can change S0 to put the stack somewhere else. For that reason, I would use TIB to pass the TIB address. Another thing you might be interested to know is that the TIB is only 82 bytes long at the top of high memory. That length is why that TI Forth won't let you type a longer line than 80 characters before reaching around you and tapping <Enter> for you, so to speak. I don't mean to challenge your solution. I think it's great! I would just change to using TIB . Oh, and don't forget that you need a number of characters to EXPECT and that it should never exceed 80 in TI Forth if you want to use the TIB and you might as well stick to 31 since that's the maximum size a word in TI Forth can be. ...lee Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted November 19, 2012 Share Posted November 19, 2012 Thanks for the clarifications Lee. This makes perfect sense. These are the kind of details that are difficult for a newcomer to glean and grasp the first time through the Forth learning process. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted November 20, 2012 Share Posted November 20, 2012 I can't answer for TI Forth, I'm afraid. However, you probably need to look up the words WORD and FIND. In TF you could do it like this: (it is probably very similar in TIF). : RUN-WORD ( -- ) CR ." Enter a word name:" TIB @ 30 EXPECT CR BL WORD FIND 0= IF ." Word not found" DROP ELSE EXECUTE THEN ; Check out the glossary for an explanation of the words. ... Well, EXPECT and EXECUTE work the same way, but WORD and FIND do not. In fact, there is no FIND in TI Forth. The equivalent in TI Forth to FIND is probably (FIND) . The TI Forth word -FIND actually calls WORD and then calls (FIND) twice, once to search the CONTEXT vocabulary and, if that fails, the CURRENT vocabulary. WORD in both Forths parses the input stream; but, whereas in TurboForth, WORD leaves an address and text-length, in TI Forth, it leaves nothing on the stack. It does, however, copy the text of the word to HERE with its length as the first byte without updating HERE . (FIND) expects 2 addresses on the stack: the address of the length byte of the word to search for (immediately followed by the word just as WORD leaves it at HERE ) and the name field address (NFA) of the first word in the dictionary to start the search, which also starts with the length byte. If the word is not found, FALSE is left on the stack. If the word is found, its parameter field address (PFA), length byte and TRUE are left on the stack. These post-execution stack effects also obtain for -FIND . If you wish to execute the word that was found, you will probably have already popped TRUE off of the stack with an IF and DROPped or otherwise processed the length byte to get at the PFA; but, you can't use EXECUTE on the PFA---you need the code field address (CFA). It's easy: You just issue CFA to convert the PFA on the stack to the word's CFA. The TI Forth equivalent of the above RUN-WORD is: : RUN-WORD ( -- ) CR ." Enter a word name:" TIB @ 30 EXPECT CR 0 IN ! -FIND 0= IF ." Word not found" ELSE DROP CFA EXECUTE THEN ; The " 0 IN ! " are necessary because the pointer to the next character of the TIB has moved to a point immediately after the just-entered word, so what gets copied to HERE will not be what the user typed unless the pointer contained in IN is reset. ...lee Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.