jschultzpedersen Posted March 24 Share Posted March 24 Hi I have run into a problem with a word, that works on it own, but not while used during autoboot. Here is the code in block 1... : MENU ( X Y SEP ADRS LEN -- ) TIB @ -ROT DUP SPAN ! 0 >IN ! VMBW >R BEGIN R@ WORD DUP WHILE >R >R 1 + 2DUP GOTOXY R> R> TYPE REPEAT 2DROP 2DROP R> DROP ; PAGE 10 10 32 S" THIS IS A TEST" MENU The result should be: THIS IS A TEST ... printed starting at X Y. The code is used to print a menu starting at location X Y and with a part of the string separated by the SEP(arator) character. As a standalone word it performs as expected, but if included in an autoboot sequence, it instead prints the first LEN characters of the block from where it is called. Here is what it is supposed to do: I initialize the TIB address in VDP and the string ADRS and LEN, use a copy of the LEN to initialize the SPAN value manually, set the >IN value to 0 and transfers the content of the string to the buffer in VDP used by WORD. I also store the LEN value on the return stack. WORD then finds the first section of the string based on the SEP. The LEN is duplicated to be used as a flag for the WHILE-REPEAT loop. If there is a substring (LEN > 0) I temporarily stores the address and length of the substring on the return stack, then uses a copy of the X Y values to locate the cursor, retrieves the substring address and length from the return stack and types the substring. When WORD returns 0 0 the WHILE-REPEAT loop is ignored, and I clean up the stack and return stack, and everyone is happy... except... The characters in the string I wish too print is correctly put at the TIB @ address and remains there at all time, but somehow during the autoboot process WORD grabs data from the first line in the block instead of from the buffer. The same thing happens if a do a 1 LOAD command. I suppose the autoboot capability basically does a 1 LOAD on DSK1, so it makes sense. I am relatively new to TurboForth. So any help is appreciated. PS. Is there anywhere, where I can get my hands on a TurboForth cartridge or an image, that can be used with FG99? Or a guide to convert the images in Classic99 to a FG99? regards Jesper 1 Quote Link to comment Share on other sites More sharing options...
atrax27407 Posted March 24 Share Posted March 24 I have always incorporated a MENU in the boot screen/ It looks like this: 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 24 Share Posted March 24 46 minutes ago, jschultzpedersen said: Hi I have run into a problem with a word, that works on it own, but not while used during autoboot. . . . The problem has to do with the fact that input to the interpreter is coming from block 1 upon autoload and from the TIB when typing on the keyboard. When you change >IN while autoloading, you change where the interpreter continues to read block 1, not the TIB. @Willsy will probably drop by later to give you a better explanation. ...lee 3 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted March 24 Author Share Posted March 24 Hi Ahh ... thanks. That explains it. Further experiments appear to show, that the issue is with the LOAD word. If I move the code to another block and do a LOAD of that block, it also behaves as you say. That puts up the question - how to trigger that first menu using the MENU word. I could hard code the menu using GOTOXY and ." WORDS, but I had hoped to use a generic WORD like MENU to make all menus from a string argument. There may be a trick. First (late night) tests indicate that it works - somewhat. Content of block 1: 2 LOAD loads a block with the MENU definition 3 LOAD loads a block with empty words named to match each line in the menu (like... : THIS ; : IS ; : A ; : LARK ;) 4 LOAD loads a block with the line THIS IS A LARK (4 valid words with single spaces in between) in the first line to the left. Then call the WORD MENU with parameters including any string of the same length as in... 10 10 32 S" 12345678901234" MENU Now, as you told me, it reads from the top line of block 4, and behold - a menu appears! Unfortunately the problem appears again the next time I need a menu. So it is a bit pointless., and even more so, because it only allows single words in a line. Is it the method of writing directly to the TIB address, that is flawed? WORD was a convenient way of interpreting the content of a string into sub strings, but as I understand it, it takes its data from the TIB address in VDP ram. I suppose I could make something similar, that took its data from CPU ram and pass the string address and length to it. regards Jesper 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 25 Share Posted March 25 I am not at home so I can't test anything but I can say that playing with TIB is not something you normally do unless you need to re-work the the way the interpreter works. I don't how the boot exact process of TurboForth but if I think about putting a menu on the screen from a BLOCK I think of ways that use very little code. Type the menu into the block with the editor with each item on a separate line. This wastes 24 chars per line but it's simple. Then something like this would print as many lines as you want up to 15 line. : .MENULINE ( n -- ) 64 * 1 BLOCK + 40 TYPE ; : MENU ( n X Y -- ) GOTOXY 0 DO I .MENULINE CR LOOP ; ( n is the number of lines you want to write) You also get fancy and organize the text in the block so that when it is written to VDP RAM it comes out the way you want it. Then it's just one line using VMBW (VDP multiple byte write) This will be instantaneous. You will need to lookup the VDP address of the screen in turboForth. It might be zero. (0) Assuming it is zero here is the code. : MENU 1 BLOCK 0 960 VMBW ; This is literally blasting 960 bytes from the block buffer to VDP RAM. 2 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted March 25 Author Share Posted March 25 Hi One of my earlier attempts was, just as you say, to use VMBW and VMBR to move data to the screen from a block. I made a generic word called VMOVE like this to be able to move data around in VDP ram using the PAD as a temporary storage... : VMOVE ( TO FROM LEN -- ) >R PAD R@ VMBR PAD R> VMBW ; With this I could copy one block to another on the same disk image just by writing: BLOCK 2 BLOCK 3 1024 VMOVE, that copies the content of block 3 to block 2. Or I could copy the content of the screen (assuming VDP address 0 as start.) by saying 2 BLOCK 0 960 VMOVE (similar to your example), or do it the other way round to display stuff on the screen. All would be well, as long as I remembered to do a UPDATE FLUSH of the buffers afterwards to save changes permanently. My MENU word was an attempt to make a generic mechanism for typing menus on screen without having to format, print and save a screen to a block manually before actually using it. I think I will focus on creating a word, that does, what WORD does, but works from a selected address in CPU ram instead of from the TIB address. In this way I can avoid the practice of messing with the TIB content and SPAN and >IN. Thanks to all for input. regards Jesper 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 25 Share Posted March 25 (edited) Ah now I remember. Mark put his disk buffers in VDP RAM. Lee and I put disk buffers in RAM which is slightly more convenient. I was going to say my system source code could help you, but it has some CODE words that are not in TurboForth and would need some re-writing in TF Assembler. It is messy because I keep playing with experiments but here is the hilevel code. You can search for PARSE and PARSE-WORD to see a possible solution. https://github.com/bfox9900/CAMEL99-ITC/blob/master/SRC.ITC/HILEVELX.HSF The topline is ISO Forth has PARSE ( char -- TIB len) which lets you take the string and do whatever you like. I added PARSE-WORD ( char -- TIB len) which skips leading spaces like word but still returns the input buffer string ( addr,len) on the data stack. It might be easier to just use WORD and take the counted string from HERE where WORD puts it and "place" it in a new string. EDIT: if you don't have 2dup: : 2DUP OVER OVER ; CREATE A$ 80 ALLOT : PLACE ( src n dst -- ) 2DUP C! 1+ SWAP CMOVE ; : PARSE$ ( addr -- ) 1 WORD COUNT ROT PLACE ; A$ PARSE$ THIS TEXT SHOULD GO INTO A$ Edited March 25 by TheBF TYPO 2 Quote Link to comment Share on other sites More sharing options...
atrax27407 Posted March 25 Share Posted March 25 FWIW, the utilities that you mentioned are availoable in TF. Have you checked Mark's TF website? Also, I simply hard-coded the menu options in block 1 and 2 so they appear on boot. Then you just select the one(s) you need with "x LOAD". 3 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted March 26 Author Share Posted March 26 Hi There are a number of extensions to the standard vocabulary in TF, that can be loaded. I have not investigated much into their capabilities, and it may well be, that they provide features, that I am reinventing for myself. As for moving data around in CPU ram, in VDP ram, and between the two, I have so far encountered VMBR, VMBW and CMOVE. But I have not seen a word to move data between two potentially overlapping VDP memory areas, which is why I cooked up the VMOVE word Please let me know if anything better is available. I learned from your code example, that 1 WORD is a good way of getting string input and transferring it to something like a reserved memory area pointed to by A$. But I still have problems with the combination of LOAD (autoload) and WORD as in this example Content of block 6: A$ CREATE 80 ALLOT : TEST3 ( ADRS -- ) 1 WORD ROT SWAP CMOVE ; ( ** This code simply moves input from TIB to the word buffer and then to adrs in CPU ram ) An example of code in block 1 loaded by autostart: ( * HERE IS A COMMENT * ) 6 LOAD A$ TEST3 THIS IS A TEST A$ 14 TYPE This produces no output. Any code after the third line is ignored. But if I then manually type: A$ 14 TYPE the output is: THIS IS A TEST So A$ is initialized as expected by the TEST3 word. This implies that LOAD loses track of further content of a block once WORD is used. Probably because it messes with >IN, that is also used by LOAD, as hinted in an earlier entry in this discussion. So I have to avoid WORD. Here is a solution that works: Change block 6 to: A$ CREATE 80 ALLOT : TEST4 ( SRC LEN DST -- ) SWAP CMOVE ; Change block 1 to: ( * HERE IS A COMMENT * ) 6 LOAD S" THIS IS A TEST" A$ TEST4 A$ 14 TYPE Autobooting will now produce the output: THIS IS A TEST This means LOAD is not interrupted processing the block, and I can include further calls to words and build my menu. Life is good! PS. My sources of information comes what I can find on turboforth.net, in the pages of Atariage, and from the two excellent books by Leo Brodie (Starting Forth & Thinking Forth). Unfortunately I know of no persons within physical range here in Denmark, that has this interest in the TI99 apart from myself. So it gets a little lonely sometimes. regards Jesper 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 26 Share Posted March 26 As you have discovered, there are many ways to skin this cat. You could also do something like this: : A$. ." THIS IS A TEST" ; CR A$. CR Of course, simply including just the “print string” ( ." ) code accomplishes the same thing. For your menu solution, you might try something like this: : MENU1 ." FIRST" CR ." SECOND" CR ." THIRD" CR ." FORTH" CR ; Pardon the intentional pun. ...lee 1 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 26 Share Posted March 26 (edited) 4 hours ago, jschultzpedersen said: Hi There are a number of extensions to the standard vocabulary in TF, that can be loaded. I have not investigated much into their capabilities, and it may well be, that they provide features, that I am reinventing for myself. As for moving data around in CPU ram, in VDP ram, and between the two, I have so far encountered VMBR, VMBW and CMOVE. But I have not seen a word to move data between two potentially overlapping VDP memory areas, which is why I cooked up the VMOVE word Please let me know if anything better is available. I learned from your code example, that 1 WORD is a good way of getting string input and transferring it to something like a reserved memory area pointed to by A$. But I still have problems with the combination of LOAD (autoload) and WORD as in this example Content of block 6: A$ CREATE 80 ALLOT : TEST3 ( ADRS -- ) 1 WORD ROT SWAP CMOVE ; ( ** This code simply moves input from TIB to the word buffer and then to adrs in CPU ram ) An example of code in block 1 loaded by autostart: ( * HERE IS A COMMENT * ) 6 LOAD A$ TEST3 THIS IS A TEST A$ 14 TYPE This produces no output. Any code after the third line is ignored. But if I then manually type: A$ 14 TYPE the output is: THIS IS A TEST I just realized I could run TF on JS99er. I did a quick test and learned something. WORD in TF is not Forth-83 standard. It returns an address, length pair. ( a stack string) FORTH-83 WORD returns a byte counted string. so this works. 1 WORD This is a test CR TYPE This is a test ok There is an old joke that goes "if you have seen one Forth... you have seen one Forth." All the DIY systems have differences that you must get familiar with. The word PLACE that I wrote up earlier has become part of the 2012 unofficial standard because it was used in many systems. PLACE takes a stack string pair and stores it memory as a byte counted string. It is the reciprocal of COUNT which takes a counted string address and converts it to a stack string pair. These are the most common way to deal with storing and using strings but there are Forth systems written in C that use 0 delimited strings and there are systems that change the count byte to a cell so you can have strings a long as memory. I know of a system that renames PLACE to $! and COUNT to $@ which is arguably more consistent with ! and @ for integers. Edited March 26 by TheBF typo 3 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 26 Share Posted March 26 18 minutes ago, TheBF said: I did a quick test and learned something. WORD in TF is not Forth-83 standard. It returns an address, length pair. ( a stack string) FORTH-83 WORD returns a byte counted string. Then, of course, there is figForth’s returning nothing except the knowledge that the counted string is at HERE , as with TI Forth and fbForth. ...lee 4 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted March 26 Author Share Posted March 26 Hi Well, it has not bothered me, since I only know how TurboForth behaves 😀 I run TF from Classic99. I would love to get my hands on a TF cartridge or an image that works with FG99. But for now it is Classic99. Anyway, here is a working solution to the task I set myself. The INNER word takes a string and a separator from some global values initiated in the OUTER word, and returns the address and length of the sub string, which I use to print the section. Eventually it runs out of text and returns 0 0. All very similar to the behaviour of WORD. But, as it all happens in CPU ram, it works during autoload, unlike WORD which apparently does not coexist very well with LOAD. There is no formatting in OUTER yet. And I will see if I can get rid of some of the global values - just on principle. 0.. VALUE NN 0 VALUE MM 0 VALUE MYADRS 0 VALUE MYLEN 32 VALUE SEP 1.: INNER 2. 0 TO MM 3. BEGIN 4. MYADRS MM + C@ SEP <> NN MYLEN < AND 5. WHILE 6. 1 +TO MM 1 +TO NN 7. REPEAT 8. NN MYLEN <= IF MYADRS MM ELSE 0 0 THEN 9. MM 1 + +TO MYADRS 1 +TO NN ; 10.: OUTER ( ADRS LEN SEP -- ) 11. TO SEP TO MYLEN TO MYADRS 0 TO NN 12. BEGIN 13. NN MYLEN < 14. WHILE INNER CR TYPE REPEAT ; 15.S" DETTE ER VIRKELIG FINE SAGER" 32 OUTER regards Jesper 4 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 26 Share Posted March 26 Try this: : MENU ( adr len -- ) 0 DO \ loop through the string DUP \ maintain address on the stack C@ DUP \ get character and copy for test BL = IF \ is it a blank? DROP CR \ yes..drop it and emit <CR><LF> ELSE EMIT \ no..emit the just-read character THEN 1+ \ advance to next character LOOP DROP ; \ clean up S" ONE TWO THREE FOUR " MENU ...lee 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 26 Share Posted March 26 (edited) 4 hours ago, jschultzpedersen said: Hi Well, it has not bothered me, since I only know how TurboForth behaves 😀 I run TF from Classic99. I would love to get my hands on a TF cartridge or an image that works with FG99. But for now it is Classic99. Anyway, here is a working solution to the task I set myself. The INNER word takes a string and a separator from some global values initiated in the OUTER word, and returns the address and length of the sub string, which I use to print the section. Eventually it runs out of text and returns 0 0. All very similar to the behaviour of WORD. But, as it all happens in CPU ram, it works during autoload, unlike WORD which apparently does not coexist very well with LOAD. There is no formatting in OUTER yet. And I will see if I can get rid of some of the global values - just on principle. 0.. VALUE NN 0 VALUE MM 0 VALUE MYADRS 0 VALUE MYLEN 32 VALUE SEP 1.: INNER 2. 0 TO MM 3. BEGIN 4. MYADRS MM + C@ SEP <> NN MYLEN < AND 5. WHILE 6. 1 +TO MM 1 +TO NN 7. REPEAT 8. NN MYLEN <= IF MYADRS MM ELSE 0 0 THEN 9. MM 1 + +TO MYADRS 1 +TO NN ; 10.: OUTER ( ADRS LEN SEP -- ) 11. TO SEP TO MYLEN TO MYADRS 0 TO NN 12. BEGIN 13. NN MYLEN < 14. WHILE INNER CR TYPE REPEAT ; 15.S" DETTE ER VIRKELIG FINE SAGER" 32 OUTER regards Jesper Working code is good by any measure. Congratulations Jepser on solving your problem. I am guessing you are a veteran programmer in other languages. and Ja, "dette er virkelig fine" is very true. It's great to see a new person interested in Forth here. Something that jumps out at me is that you defined SEP as 32 and Forth defines the constant BL = 32, so you could remove that VALUE if it's never going to change. Edited March 26 by TheBF typo 3 Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 27 Share Posted March 27 (edited) Interesting discussion. Yeah, sometimes early, naive design decisions come back to bite you in the ass. Placing blocks (and the TIB) into VDP turned out to be more trouble than it was worth IMO. The key to understanding this is to know that there is some magic that takes place in WORD. Here is it's implementation: ; WORD ( delimiter -- address length ) ; Moves through TIB in VDP memory, discarding leading delimiters, looking for ; a word. A word is identified when a trailing delimiter is detected. ; The word is copied from VDP to CPU memory. ; Pushes the start address of the word (in CPU memory), and the length of ; the word to the stack. ; If no word is found (for example if we hit the end of the TIB without ; detecting a word then 0 0 is pushed on the stack. wordh data typcmh,4 text 'WORD' word data docol ; tib @ blk @ ?dup if nip block then data tib_,fetch word0 data blk,fetch,qdup,zbrnch,word2,nip,fblock word2 data word1 data exit ; at this point, data stack is ( delimeter address -- ) ; where address is the address in vdp to start searching from. ; address is either TIB+>IN (if BLK=0) or block address+>IN ; if BLK>0. (the code to add >IN to the address is in _word) word1 data $+2 bl @bank1 data _word ; see 1-08-Parsing.a99 Note the second part, where it is stated that "address is either TIB + >IN (if BLK=0) or block address + >IN if BLK>0." So despite manipulating >IN, it is adding the VDP address of the block and reading from there. A potential work around would be to temporarily set BLK to 0 (BLK 0!) and restore it later. That would probably work. It would fool WORD into thinking its no longer reading from a block, and therefore would use the address of TIB + >IN to read from. Its a bit of a mess! On the other hand, you can have six disk blocks in VDP memory on a 32K 4A which is pretty cool. Well, I thought it was at the time Edited March 27 by Willsy 4 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted March 29 Author Share Posted March 29 Hi TheBF... You are right about me having programmed quite a lot 😀. I have no formal education in the craft, but it has been a hoppy for me to tinker with all sorts of programming languages over the last 40 years, privately and in my work life. Interestingly it was the purchase of a TI99/4A, that initiated my career in the it world at a late age of 25. I was actually educated in economics, but computing was so much more interesting. So I made a choice and picked the right horse, so to speak. Willsy... Thanks for the explanation. I have bypassed the limitation by making a word, that is similar to WORD, but works exclusively in CPU ram. I am still polishing the solution, but I wil publish it when done. By the way... There may be an issue with autoboot. Try this in block 1: 1 2 3 4 .S Do an autoboot with a cold or a warm restart. It returns 2 3 4 in the stack. The bottom stack entry is lost. Do a 1 LOAD. It returns 1 2 3 4 as it should. I can sort of bypass the problem by adding a dummy stack entry at the bottom. So for instance 0 1 2 3 4 will leave 1 2 3 4 in the stack if 1 2 3 4 is what I want. But then 1 LOAD has five entries on the stack. So this might be a bug. Lee Stewart... Yes, that is obviously simpler and faster than my solution. My intention was to use any separator for multi-word entries, but that can also be included in your code. So it will be the standard by which I shall measure my next version 😀 regards jesper 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 29 Share Posted March 29 (edited) 2 hours ago, jschultzpedersen said: Hi TheBF... You are right about me having programmed quite a lot 😀. I have no formal education in the craft, but it has been a hoppy for me to tinker with all sorts of programming languages over the last 40 years, privately and in my work life. Interestingly it was the purchase of a TI99/4A, that initiated my career in the it world at a late age of 25. I was actually educated in economics, but computing was so much more interesting. So I made a choice and picked the right horse, so to speak. Ok so I am going to take "quite a lot" as: you know programming very well. I suspect you have now travelled "through the looking glass" and are intrigued by this weird programming language. My after retirement time has been spent learning the stuff about Forth that I didn't understand 35 years ago when I got TI-Forth. How to write a Forth, how to write a Forth in Forth , writing a Forth cross-compiler and writing a Forth in Forth. For your WORD replacement project here are some words that showed up in the late 1990s. I don't know who invented them. /STRING It's not in the TF kernel but is easily defined and is now an ISO Forth word. SKIP and SCAN are not in the standard but are in GNU Forth, most commercial Forths and many others. /STRING is simple yet remarkably useful for strings that live on the data stack as an addr,length pair. : /STRING ( caddr1 u1 n - caddr2 u2 ) TUCK - >R + R> ; It is used like this : S" This is a string of arbitrary length" 5 /STRING CR TYPE <enter> Returns: is a string of arbitrary length ok We can use /STRING to make SKIP and SCAN. (These are weird with multiple WHILE statements but think of WHILE as form of IF) : SKIP ( addr len char -- addr' len') >R BEGIN DUP \ is length >0 WHILE OVER C@ R@ = \ 1stchar = to char ? WHILE 1 /STRING \ cut 1 char, update addr,len REPEAT THEN R> DROP ; Test with S" ***********TESTING 1 2 3" ASCII * SKIP TYPE : SCAN ( addr len char -- addr' len') >R BEGIN DUP \ is length >0 WHILE OVER C@ R@ <> \ 1stchar <> to char ? WHILE 1 /STRING \ cut 1 char, update addr,len REPEAT THEN R> DROP ; In each of these words, the output string is left on the data stack as an address,length pair so you can re-run more string processing. Example: -TRAILING removes trailing spaces from a string, therefore this works. : CLEAN$ -TRAILING BL SKIP ; S" HELLO " CLEAN$ TYPE Hope you find this useful. Edited March 29 by TheBF fixed comment on SKIP 3 Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 31 Share Posted March 31 On 3/29/2023 at 12:02 PM, jschultzpedersen said: By the way... There may be an issue with autoboot. Try this in block 1: 1 2 3 4 .S Do an autoboot with a cold or a warm restart. It returns 2 3 4 in the stack. The bottom stack entry is lost. Do a 1 LOAD. It returns 1 2 3 4 as it should. I can sort of bypass the problem by adding a dummy stack entry at the bottom. So for instance 0 1 2 3 4 will leave 1 2 3 4 in the stack if 1 2 3 4 is what I want. But then 1 LOAD has five entries on the stack. So this might be a bug. Yep. That looks like a bug to me. TF reads the keyboard just before performing an autoboot. The idea is you can change, or bypass the autoload feature. So, after selecting TurboForth from the cartridge menu, if you quickly press and hold a key, it will boot from DSK<the-key-you-pressed>.BLOCKS So, if you hold down 2, it will look for DSK2.BLOCKS. If you hold down A it will look for DSKA.BLOCKS etc. If you hold down ENTER, it will bypass autoload entirely. The autoload stuff at startup is written in Forth. My guess is, it's calling KEY to get the code of the pressed key, and subsequently DROPs it somewhere. That's fine.... IF you pressed a key! If not, there was nothing on the stack to drop. My guess is it's related to that. That, or the data stack pointer is not correctly initialised. Might be interesting to try and track it down. It's interesting that all the code in block 1 of the standard boot disk runs just fine. One would expect it to barf. Hmmm... But yes, just use a dummy value for now. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 31 Share Posted March 31 (edited) 7 hours ago, Willsy said: The autoload stuff at startup is written in Forth. My guess is, it's calling KEY to get the code of the pressed key, and subsequently DROPs it somewhere. That's fine.... IF you pressed a key! If not, there was nothing on the stack to drop. My guess is it's related to that. That, or the data stack pointer is not correctly initialised. Might be interesting to try and track it down. It's interesting that all the code in block 1 of the standard boot disk runs just fine. One would expect it to barf. Hmmm... I have not figured out exactly what is going on, but I am accustomed to SP@ and S0 pointing to the same address when I start with an empty stack. However, SP@ points to >A2C4, whereas S0 points to >A2C6. Using 1 2 3 4 .S on block 1 shows 2 3 4 <TOP with >0001 sitting at >A2C4—as though S0 had started out at >A2C6, but somehow got decremented after autoboot. ...lee Edited March 31 by Lee Stewart correction 1 Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted April 1 Author Share Posted April 1 Hi theBF... This code construct - WHILE flag some code THEN - is not mentioned in the online guide on turboforth.net. It seems to work like a classic WHILE - WEND loop. Nice to know... I thought that THEN was only of use with IF. My MENU project is going well. - which means I have working code if not minimalistic code 😀. I have toyed with different ways of moving stack content around and moving some data from the stack to the return stack or as 'local' data (see code) to simplify the stack gymnastics. The current incarnation cuts out a word from a string and returns info about the rest of the string as well as the selected substring based on a specified separator. I then use a crude form of local variables for x and y coordinates in the wrapper word MENU to print each line provided by CUT at a chosen location. I do not know if messing with the content of H is considered good form, but hey, it works. I know there is a code module for local variables included with Turboforth. I just wanted to see, if I could do without. And I am having fun... It reminds me of my late twenties, when, in my first job, I encountered the similarly weird and powerful APL language. Like Forth it was also a stack oriented language where you read from right to left and invented your own 'words' along the way and topped it off with some powerful matrix and algebra operators and a custom keyboard. And like Forth it was very hard to read other peoples code without supporting comments or plenty of time to dissect the code. Block 50 Mode:OVER 0 1 2 3 4 5 6 .................................................................. 0.. DROPSTACK ( -- ) . 1. DEPTH 0 DO DROP LOOP ; . 2. . 3.: CUT ( SEP ADRS LEN -- SEP NEWADRS NEWLEN ADRS LEN ) . 4. DUP 0 = IF DROPSTACK 0 0 0 EXIT THEN . 5. 0 ROT . 6. BEGIN . 7. DUP C@ 4 PICK <> 3 PICK 3 PICK 1+ > AND . 8. WHILE . 9. 1+ SWAP 1+ SWAP . 10. REPEAT . 11. 1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP . 12. 2 PICK OVER - SWAP 2 PICK 0 <> + ; . 13. . 14. --> . 15. . Block 51 Mode:OVER 0 1 2 3 4 5 6 .................................................................. 0.: MENU ( SEP ADRS LEN X Y -- ) . 1. 4 ALLOT HERE 2 - ! HERE 4 - ! . 2. BEGIN . 3. DUP 0 <> . 4. WHILE . 5. CUT HERE 4 - @ HERE 2 - @ GOTOXY TYPE . 6. HERE 2 - @ 1 + HERE 2 - ! . 7. REPEAT . 8. DROPSTACK HERE 4 - H ! ; . 9. . Like you I am also retired, and instead of getting bored, I started fulfilling my bucket list. One of the items on the list was going back to my first computer love... aka. the TI99/4A. And here I am some years later. By the way. I have written a number of articles for the TI*MES magazine in England on Extended Basic in the last few years as a member of the TIUGUK user group. I also wrote one about LOGO II from the viewpoint of a newcomer to that language, and I have done one article on TurboForth, also from a newcomers viewpoint, that has yet to be published. I do not suppose there are many readers, but I enjoy writing, and it is a good way to learn the stuff yourself. regards Jesper 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 2 Share Posted April 2 5 hours ago, jschultzpedersen said: Hi theBF... This code construct - WHILE flag some code THEN - is not mentioned in the online guide on turboforth.net. It seems to work like a classic WHILE - WEND loop. Nice to know... I thought that THEN was only of use with IF. My MENU project is going well. - which means I have working code if not minimalistic code 😀. I have toyed with different ways of moving stack content around and moving some data from the stack to the return stack or as 'local' data (see code) to simplify the stack gymnastics. The current incarnation cuts out a word from a string and returns info about the rest of the string as well as the selected substring based on a specified separator. I then use a crude form of local variables for x and y coordinates in the wrapper word MENU to print each line provided by CUT at a chosen location. I do not know if messing with the content of H is considered good form, but hey, it works. I know there is a code module for local variables included with Turboforth. I just wanted to see, if I could do without. And I am having fun... It reminds me of my late twenties, when, in my first job, I encountered the similarly weird and powerful APL language. Like Forth it was also a stack oriented language where you read from right to left and invented your own 'words' along the way and topped it off with some powerful matrix and algebra operators and a custom keyboard. And like Forth it was very hard to read other peoples code without supporting comments or plenty of time to dissect the code. Block 50 Mode:OVER 0 1 2 3 4 5 6 .................................................................. 0.. DROPSTACK ( -- ) . 1. DEPTH 0 DO DROP LOOP ; . 2. . 3.: CUT ( SEP ADRS LEN -- SEP NEWADRS NEWLEN ADRS LEN ) . 4. DUP 0 = IF DROPSTACK 0 0 0 EXIT THEN . 5. 0 ROT . 6. BEGIN . 7. DUP C@ 4 PICK <> 3 PICK 3 PICK 1+ > AND . 8. WHILE . 9. 1+ SWAP 1+ SWAP . 10. REPEAT . 11. 1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP . 12. 2 PICK OVER - SWAP 2 PICK 0 <> + ; . 13. . 14. --> . 15. . Block 51 Mode:OVER 0 1 2 3 4 5 6 .................................................................. 0.: MENU ( SEP ADRS LEN X Y -- ) . 1. 4 ALLOT HERE 2 - ! HERE 4 - ! . 2. BEGIN . 3. DUP 0 <> . 4. WHILE . 5. CUT HERE 4 - @ HERE 2 - @ GOTOXY TYPE . 6. HERE 2 - @ 1 + HERE 2 - ! . 7. REPEAT . 8. DROPSTACK HERE 4 - H ! ; . 9. . Like you I am also retired, and instead of getting bored, I started fulfilling my bucket list. One of the items on the list was going back to my first computer love... aka. the TI99/4A. And here I am some years later. By the way. I have written a number of articles for the TI*MES magazine in England on Extended Basic in the last few years as a member of the TIUGUK user group. I also wrote one about LOGO II from the viewpoint of a newcomer to that language, and I have done one article on TurboForth, also from a newcomers viewpoint, that has yet to be published. I do not suppose there are many readers, but I enjoy writing, and it is a good way to learn the stuff yourself. regards Jesper Oh Jesper. We now know you are cut from special cloth. APL programmers are right out there on the edge with Forth and LISP programmers BUT closer to the edge. There is a guy in the USA who wrote an APL type system on top of Forth, CoSy Forth. I am not sure he will ever be healthy again. Glad to hear that you are also enjoying retirement. With you background your article on Forth will be fun to read. Yes IF is typically resolved with THEN but here are the definitions of the standard branching and looping words in ANS/ISO Forth. Notice the definition for WHILE. (POSTPONE is the ANS/ISO word to do the actions of COMPILE and [COMPILE] in TF/FORTH-83) Spoiler : AHEAD ( -- addr) HERE 0 , ; : <BACK ( addr --) HERE - , ; : THEN ( addr -- ) HERE OVER - SWAP ! ; IMMEDIATE : BEGIN HERE ; IMMEDIATE : IF POSTPONE ?BRANCH AHEAD ; IMMEDIATE : ELSE POSTPONE BRANCH AHEAD SWAP POSTPONE THEN ; IMMEDIATE : UNTIL POSTPONE ?BRANCH <BACK ; IMMEDIATE : AGAIN POSTPONE BRANCH <BACK ; IMMEDIATE : WHILE POSTPONE IF SWAP ; IMMEDIATE : REPEAT POSTPONE AGAIN POSTPONE THEN ; IMMEDIATE " I do not know if messing with the content of H is considered good form, but hey, it works." Playing with the dictionary pointer is fair game in Forth. That's what ALLOT does after all. Your creative use of HERE and ALLOT for local variables however is just delaying the inevitable, which is learning to use the data stack and return stack to do your wishes. To paraphrase Stars Wars: "USE THE FORTH JESPER" The data stack is challenging but it is part of the fun of this crazy thing. Learning to use it as Chuck Moore intended takes some time. Some general rules of good Forth form that I can think of. - Never try to manage more than 3 elements on the data stack in a word definition (some exceptions apply of course but usually >3 means the code needs factoring) - Global variables are not a crime any more than they are in Assembly language and Forth is the Assembler for the Forth VM. With the data stack , you need very few of them anyway. - Factor the code into small definitions to simplify stack manipulation and create a "language" of Forth "WORDS" to describe the parts of the problem. Then use the words to solve the problem. (nothing new to you with APL experience) This line is something only really smart people (APL programmers? ) can understand and would typically be factored into named definitions (IF it was really needed) 1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP 2 PICK OVER - SWAP 2 PICK 0 <> + ; I am going to see if I can re-write your code in more idiomatic Forth just to see if I can. I will test it with TurboForth like I did with my other examples. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 2 Share Posted April 2 I am not sure that is what you wanted to do exactly but if you had a bunch of text in a block, typing: 1 BLOCK 128 2 2 AT .MENU would print a menu from the text in the block. A rule of thumb in the Forth world that I forgot to mention goes: "Don't hide your tools" meaning make words that you can re-use. So most of the words below would have uses in other parts of a project. But to be honest I think people would normally build the menu with text and Forth code in the block and then just LOAD the block. No new code required. PAGE .( Main Menu for Purposes Unknown) CR CR .( 1 Stop all wars) CR .( 2 Move the moon 5 Metres farther away) CR .( 3 Increase the speed of light by 10%) CR CR .( Press the ANY key to continue) Spoiler : 2SWAP ( a b c d -- c d a b ) ROT >R ROT R> ; : /STRING ( caddr1 u1 n - caddr2 u2 ) TUCK - >R + R> ; : SCAN ( addr len char -- addr' len') >R BEGIN DUP WHILE OVER C@ R@ <> WHILE 1 /STRING REPEAT THEN R> DROP ; : SKIP ( addr len char -- addr' len') >R BEGIN DUP WHILE OVER C@ R@ = WHILE 1 /STRING REPEAT THEN R> DROP ; \ find position of char in string : POS ( addr len char -- addr len n ) >R 2DUP R> SCAN NIP OVER SWAP - ; : /CHOP ( addr len n -- addr len addr2 len2 ) >R OVER R@ 2SWAP R> /STRING 2SWAP ; VARIABLE X VARIABLE Y : AT ( x y -- ) Y ! X ! ; : .TEXT ( adr len --) X @ Y @ GOTOXY TYPE ; : .MENU ( ADRS LEN sep -- ) >R BEGIN R@ SKIP \ skip leading separators DUP 0 <> WHILE R@ POS /CHOP .TEXT 2 Y +! REPEAT R> DROP 2DROP ; PAGE S" ITEM-1 ITEM-2 ITEM-3 ITEM-4 ITEM-5 " 5 5 AT BL .MENU Quote Link to comment Share on other sites More sharing options...
jschultzpedersen Posted April 2 Author Share Posted April 2 Hi theBF... I could use blocks. But they are static. I want my menu word to be dynamic. That is - put its payload wherever I want and be able to modify it as I go along - like showing different content depending on some action taken by the user. I already have a word, that clears a limited area of the screen and put a nice border around it, without effecting the rest of the screen, and in that area I want to put my menu. In this way I can maintain multiple menus on the same screen, or use the areas like windows of text in a screen. So I will keep your advices in mind and se if I can refactor my code and stay within the - max 3 items in stack - rule. I'll be back! regards Jesper 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 2 Share Posted April 2 5 hours ago, jschultzpedersen said: Hi theBF... I could use blocks. But they are static. I want my menu word to be dynamic. That is - put its payload wherever I want and be able to modify it as I go along - like showing different content depending on some action taken by the user. I already have a word, that clears a limited area of the screen and put a nice border around it, without effecting the rest of the screen, and in that area I want to put my menu. In this way I can maintain multiple menus on the same screen, or use the areas like windows of text in a screen. So I will keep your advices in mind and se if I can refactor my code and stay within the - max 3 items in stack - rule. I'll be back! regards Jesper I have no doubt about you coming back. Look forward to seeing what you develop. 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.