GDMike Posted November 24, 2022 Author Share Posted November 24, 2022 7 hours ago, TheBF said: Did you ever wish you had a computer to help you organize your stack parameters better. Wish no more! Forth Wizard (sovietov.com) Good tool. Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5160488 Share on other sites More sharing options...
Willsy Posted November 24, 2022 Share Posted November 24, 2022 12 hours ago, TheBF said: Did you ever wish you had a computer to help you organize your stack parameters better. Wish no more! Forth Wizard (sovietov.com) Try this one: n1 n2 n3 n4 n5 -- n5 n4 n3 n2 n1 Crashes it hard! 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5160604 Share on other sites More sharing options...
+Lee Stewart Posted November 24, 2022 Share Posted November 24, 2022 7 hours ago, Willsy said: Try this one: n1 n2 n3 n4 n5 -- n5 n4 n3 n2 n1 Crashes it hard! Nope! Let it do 10 words: swap rot >r rot >r rot r> r> swap rot It takes a while (72 s), but eventually comes up with the above solution. ...lee 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5160630 Share on other sites More sharing options...
Willsy Posted November 24, 2022 Share Posted November 24, 2022 I'll have to try a different browser. It crashed Brave no matter how many words I allowed it. 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5160688 Share on other sites More sharing options...
+TheBF Posted November 27, 2022 Share Posted November 27, 2022 While studying on how I could "improve" Mark's SAMS code compiler, (still wondering if it's possible ) I realized that using the TF BRANCH word was the key to making tail-call optimization in Indirect threaded Forth. This version should be translatable to the other systems if one wanted to do it. I am using the manual version name that Chuck Moore used in his Machine Forth. The next trick would be to make it smart enough to know if the last word in a definition can be jumped to directly. I did some preliminary testing for COLON and CODE words but it failed on DEFER. (sounds familiar) Edit: GOTO changed. We do not need to auto-increment the *IP register because we are jumping somewhere else. This made it a bit faster \ tail call optimizizing semicolon from machine forth nov 27 2022 \ For ITC Forth HEX CODE GOTO C259 , ( *IP IP MOV,) NEXT, ENDCODE : CELL- 2- ; : PREVXT ( -- XT) HERE CELL- @ ; \ fetch the XT of previous compiled word : -; ( -- ) \ programmer controlled PREVXT >BODY \ get previous XT, compute data field -2 ALLOT \ erase the previous XT POSTPONE GOTO , \ compile the address for GOTO POSTPONE [ \ turn off compiler REVEAL ?CSP ; IMMEDIATE \ -; does not end with EXIT because it is branching directly to another \ list of tokens. That other list will end in EXIT or NEXT. I tried it out on a 1,000,000 nest/unnest benchmark that I found here: The ultimate Forth Benchmark (theultimatebenchmark.org) (Section 13.7) Normal time is : 2:30.7 With tail-call optimization: 1:55.5 1:54.06 In another test I did 1 call per definition on 20 definitions. Normal semi-colon was 1533 uS. Optimized is 873 852uS. The TAIL* definitions compile to continuous calls to NEXT and the return stack doesn't move! Spoiler \ TAIL CALL OPTIMIZATION TEST NEEDS -; FROM DSK1.TAILCALL : ACTION ; : NEST1 ACTION ; : NEST2 NEST1 ; : NEST3 NEST2 ; : NEST4 NEST3 ; : NEST5 NEST4 ; : NEST6 NEST5 ; : NEST7 NEST6 ; : NEST8 NEST7 ; : NEST9 NEST8 ; : NEST10 NEST9 ; : NEST11 NEST10 ; : NEST12 NEST11 ; : NEST13 NEST12 ; : NEST14 NEST13 ; : NEST15 NEST14 ; : NEST16 NEST15 ; : NEST17 NEST16 ; : NEST18 NEST17 ; : NEST19 NEST18 ; : TAIL1 ACTION -; : TAIL2 TAIL1 -; : TAIL3 TAIL2 -; : TAIL4 TAIL3 -; : TAIL5 TAIL4 -; : TAIL6 TAIL5 -; : TAIL7 TAIL6 -; : TAIL8 TAIL7 -; : TAIL9 TAIL8 -; : TAIL10 TAIL9 -; : TAIL11 TAIL10 -; : TAIL12 TAIL11 -; : TAIL13 TAIL12 -; : TAIL14 TAIL13 -; : TAIL15 TAIL14 -; : TAIL16 TAIL15 -; : TAIL17 TAIL16 -; : TAIL18 TAIL17 -; : TAIL19 TAIL18 -; DECIMAL : .uS 213 10 */ . ." uS" ; : TEST1 TMR@ NEST19 TMR@ - .uS ; \ 1533uS : TEST2 TMR@ TAIL19 TMR@ - .uS ; \ 852uS 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5162127 Share on other sites More sharing options...
+TheBF Posted November 28, 2022 Share Posted November 28, 2022 This code is a more complete version that you can turn the optimizing on or off with a variable set to true/false, or just use the manual version as you see fit. The smart semi-colon will only optimize CODE words or COLON definitions if they are the last word before the semi-colon. I realize this is an academic thing. Not really needed. This was mostly my need to understand how you would do this in an ITC Forth system. It works for most code, but there are things that it breaks. That is why it needs TAILCALL ON / TAILCALL OFF, which is not very practical except for a new project. \ tail call optimizizing semicolon for Camel99 Forth Nov 27 2022 Brian Fox HEX CODE GOTO C259 , ( *IP IP MOV,) NEXT, ENDCODE : CELL- 2- ; : PREVXT ( -- XT) HERE CELL- @ ; \ fetch the XT of previous compiled word : -; ( -- ) \ programmer controlled PREVXT >BODY \ get previous XT, compute data field -2 ALLOT \ erase the previous XT POSTPONE GOTO , \ compile the address for GOTO POSTPONE [ \ turn off compiler REVEAL ?CSP ; IMMEDIATE : CODE? ( xt -- ?) DUP @ CELL- = ; : COLON? ( xt -- ?) @ ['] DOCOL @ = ; VARIABLE TAILCALL \ control tail call optimizizing with this variable \ TAILCALL ON turns optimizer on : TAILCALL? ( xt --?) DUP CODE? SWAP COLON? OR TAILCALL @ AND ; : ; ( -- ) PREVXT TAILCALL? IF POSTPONE -; ELSE POSTPONE ; THEN ; IMMEDIATE \ -; does not end with EXIT because it is branching directly to another \ list of tokens. That other list will end in EXIT or NEXT. 1 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5162492 Share on other sites More sharing options...
+TheBF Posted November 29, 2022 Share Posted November 29, 2022 For your reading pleasure. Found on comp.lang.forth "Forth's most important feature has little to do with the fact that it is a stack language, it has instead to do with the way it interacts as a whole with the user. Forths extensibility, structure, modularity and very simple syntax are key attributes that give the programmer freedom to structure solutions for problems in ways that programmers of other languages cannot understand or attempt." An Interview with Tom Zimmer (wordpress.com) 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5163109 Share on other sites More sharing options...
+TheBF Posted December 4, 2022 Share Posted December 4, 2022 If you have ever wondered why GPL is slower than Forth on TI-99, this short lecture by Dr. Brad Rodriguez might explain why. Brad is the author of Camel Forth. In the lecture he explains his work on making a version that uses byte-codes ("token-threading) on the MSP430. A byte-code Forth operates in a similar way to GPL in that Forth "instructions" are abstracted to single bytes. Spoiler alert: The conventional "address threading" method (like Forth in this neighbourhood) is about 2x faster than byte-codes on MSP430. I would expect a similar result for TMS9900. 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5166015 Share on other sites More sharing options...
+TheBF Posted January 19, 2023 Share Posted January 19, 2023 I have continued trying to debug my file to compile Forth to SAMS using more CODE words with limited success. It's very appealing because I got the call overhead down to under 0.3%, the and dictionary headers are only 4 cells, with the rest of the definition going into SAMS. That is 5X faster than the Forth versions which have about 1.6% call overhead dictionary entries that are 8 CELLS total size At one point I went back to Mark's orginal code and implemented my ideas in Forth. In the file below I have back-migrated what I did so that it runs on Turbo Forth. I can't remember if I posted something like this before, but I think this version is cleaner and uses 672 bytes of dictionary. If you need a lot SAMS pages for Forth code this version saves valuable dictionary space because it doesn't need the array of dictionary pointers. The dictionary pointers are maintained in each SAMS page in the last memory location. The changes are: Replace the array of dictionary pointers with pointer at the end of the each SAMS page Write a magic number in each SAMS page to indicate page is ready to use (not used but it's there) BANKS now performs the initialization of each SAMS memory page Spoiler \ COMPILE TO SAMS MEMORY for TurboForth: \ http://turboforth.net/resources/sams.html \ BFox changes: \ - Replace array _HERES with pointer at the end of the each SAMS page \ - Write a magic number in each SAMS page to indicate page is ready to use \ - BANKS now performs the initialization of each page create _bnkstk 20 cells allot \ bank stack $F9F9 _bnkstk ! \ force first entry on bank stack to $F9 \ SAMS CARD management HEX 3000 CONSTANT CSEG \ MASTER REFERENCE CODE SEG WINDOW \ SAMS memory addresses for management variables CSEG 0FFE + CONSTANT SAMSDP \ variable at end of SAMS page SAMSDP 2- CONSTANT SAMSID \ ID field that this is a valid page ABBA CONSTANT MAGIC# _bnkstk value _bsp \ pointer into bank stack -1 value _bank \ current bank 0 value _nhere \ "normal" here 0 value _maxBank : CMAP ( n -- ) CSEG >MAP ; \ "code map" : >bank ( n --) 2 +to _bsp dup _bsp ! CMAP ; : bank> ( -- n) -2 +to _bsp _bsp @ CMAP ; \ reserve space for here pointers for n banks : BANKS ( n -- ) DUP TO _MAXBANK DUP 1+ 0 DO I CMAP CSEG SAMSDP ! MAGIC# SAMSID ! LOOP CR 4 * U. ." K SAMS code" CR ; : b: ( n -- ) \ begin compiling a banked definition in bank n _bank -1 <> if : \ runtime stuff compile lit _bank , compile >bank compile branch SAMSDP @ dup , \ compile time stuff here to _nhere \ save "normal here" h ! \ set h to _bank's "here" _bank CMAP \ map in the bank else : then ; : _bfree ( -- ) \ print free memory in the bank... $4000 SAMSDP @ - . ." bytes free." cr ; : ;b ( -- ) \ end banked compilation compile branch _nhere , here SAMSDP ! \ update here for bank _bfree _nhere h ! \ restore h to "normal" memory compile bank> [compile] ; ; : : ( -- ) _bank -1 = if : else b: then ; -1 TO _BANK : ; ( -- ) _bank -1 = if [compile] ; else ;b then ; immediate : setBank ( bank -- ) to _bank _BANK -1 _maxBank within 0= abort" Illegal bank number" _BANK -1 = if cr ." Compiling to 32K memory." cr EXIT then cr ." Bank " _bank . ." active. " _bfree ; HERE SWAP - DECIMAL . .( bytes) Test code Spoiler DECIMAL 32 BANKS HERE 16 SETBANK : TEST CR ." This has got to work!" ; 17 SETBANK : HELLO CR ." Hello from SAMS" ; 18 SETBANK : NESTED1 ." nesting 1" ; 19 SETBANK : NESTED2 NESTED1 ." 2 " ; 20 SETBANK : NESTED3 CR NESTED2 ." 3 " CR TEST ; 16 SETBANK ( Use 240 again for a circular nest) : GO NESTED3 HELLO ; HERE SWAP - . .( bytes) GO 4 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5192987 Share on other sites More sharing options...
+TheBF Posted January 28, 2023 Share Posted January 28, 2023 @oddemann demonstrated a BASIC program he had generated by ChatGPT. This post is for people who might want to try Forth but aren't sure what it's like. I am always curious how Forth compares to BASIC for various programming tasks. The Forth version of this program takes advantage of the word #INPUT which in a library for Camel99 Forth. #INPUT gives Forth an easy to use INPUT statement for numbers, that behaves like TI BASIC INPUT but for numbers only. (It would not be hard to make a version of #INPUT for any Forth system. I would do it for anyone who wanted it) TI BASIC Version 1 rem written by ChatGPT 2 rem minor changes for TI-BASIC 10 RANDOMIZE 20 N=INT(RND*10)+1 30 PRINT "I'm thinking of a number between 1 and 10." 40 INPUT "What is your guess? ": GUESS 50 IF GUESS=N THEN 90 60 PRINT "Sorry, that's not the number I was thinking of." 70 PRINT "Try again!" 80 GOTO 40 90 PRINT "Congratulations! You guessed the number!" 100 END Things to Notice about Forth vs BASIC Forth systems use files of source code (text) to add functionality to the language. See the INCLUDE statements. Different Forth systems will stuff more or less into the base system depending on the implementer's taste. Camel99 is a minimal system so typically needs more "INCLUDE" statements. Forth has no GOTO statement. It uses structured looping (BEGIN WHILE REPEAT and others) That can be adjustment when you are used to BASIC. Forth only understands what you have already told it so variables must be declared before you can use them. Newlines only happen if you want them. CR means "carriage return" from the days of the teletype. I have heard that ChatGPT writes crappy Forth code. I guess I should ask it myself. Here is my Forth equivalent program. INCLUDE DSK1.RANDOM INCLUDE DSK1.INPUT VARIABLE N VARIABLE GUESS : RUN RANDOMIZE 10 RND 1+ N ! CR ." I'm thinking of a number between" CR ." 1 and 10." BEGIN CR ." What is your guess? " GUESS #INPUT GUESS @ N @ <> WHILE CR ." Sorry, that's not the number I was thinking of." CR ." Try again!" REPEAT CR ." Congratulations! You guessed the number!" CR ." ** DONE ** " ; 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5198154 Share on other sites More sharing options...
GDMike Posted January 28, 2023 Author Share Posted January 28, 2023 Hope to hear from "Dave", our newest TF learner! I've invited him here as he is trying to learn. Ask away Dave and welcome! 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5198184 Share on other sites More sharing options...
+TheBF Posted February 17, 2023 Share Posted February 17, 2023 I thought I would summarize the work on the Byte Magazine Sieve Benchmark with my attempts to make it faster in Forth. I put the information here since it applies to any of the TI-99 Forth systems. It's can be a surprise to see what really makes programs go faster. Something I find interesting is that putting all the data into VDP RAM only slowed this program down by 12%. The sieve is something of a worst case because we are hitting the VDP RAM one byte at a time, except for the initial fill with '1'. If you use VDP RAM for bigger data structures that deal with chunks of memory, it would be even less of a performance hit. Forth strings like ." and S" could arguably be kept in VDP RAM with not too much performance reduction. We can also see that a lot is gained by making words to index and read/write the array faster. 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5211035 Share on other sites More sharing options...
+TheBF Posted March 18, 2023 Share Posted March 18, 2023 (edited) From some discussion on comp.lang.forth I got this link: rabbithat/Takeuchi_function: Used in benchmarking uLisp. Here translated to mecrisp-stellaris Forth for a comparison benchmark (github.com) This gives me for the first time a comparison of uLISP which a very neat system for writing apps on limited hardware and Forth doing the same thing on the same CPU. To be fair I am sure there are optimizations that could be done with uLISP like making macros that could speed it up so we should just compare the vanilla versions. You can read it for yourself but here is the topline. I pretty was surprised at the difference between uLISP and Forth. Mecrisp Forth is a pretty fast implementation in native code. uLISP 8,102 milliseconds Forth 134 milliseconds C 30.5 milliseconds As for comparing Ardunio to our beloved TI-99 Camel99 Forth ... 99.36 seconds. Spoiler \ Takeuchi function benchmark \ ---------------------------------------------------------------------------------------- \ \ Lisp definition of Takeuchi function (Copied from: http://www.ulisp.com/show?1EO1): \ (defun tak (x y z) \ (if (not (< y x)) \ z \ (tak \ (tak (1- x) y z) \ (tak (1- y) z x) \ (tak (1- z) x y)))) \ ---------------------------------------------------------------------------------------- \ Equivalent Forth definition of Takeuchi function: \ Edited for Camel99 Forth NEEDS ELAPSE FROM DSK1.ELAPSE \ timer \ Stuff not in Camel99 kernel : ROLL \ nn..n0 n -- nn-1..n0 nn ; 6.2.2150 DUP>R PICK SP@ DUP CELL+ R> 1+ CELLS MOVE DROP ; : >= S" 1- >" EVALUATE ; IMMEDIATE \ ------------------------------------------------ \ ( x y z -- x y z boolean) : NOT_Y<X? 1 PICK 3 PICK >= ; \ ( x y z -- z ) : ONLY_Z SWAP DROP SWAP DROP ; \ ( x y z -- x y z x-1 y z) : TAKX 2 PICK 1- 2 PICK 2 PICK ; \ ( x y z result1 -- x y z result1 y-1 z x) : TAKY 2 PICK 1- 2 PICK 5 PICK ; \ ( x y z result1 result2 -- result1 result2 (z-1) x y) : TAKZ 2 ROLL 1- 4 ROLL 4 ROLL ; \ ( x y z -- result ) : TAK NOT_Y<X? IF ONLY_Z ELSE TAKX RECURSE TAKY RECURSE TAKZ RECURSE RECURSE THEN ; \ 18 12 6 TAK are the benchmark arguments However the Forth code has really not translated the algorithm very well. We can speed this up with proper word substitutions: 1 PICK should be OVER SWAP DROP should be NIP And Camel99 has the words 3RD and 4TH to replace 2 PICK and 3 PICK Then the time is: 85.53 seconds. Spoiler \ Edited for Camel99 Forth NEEDS ELAPSE FROM DSK1.ELAPSE \ timer \ Stuff not in Camel99 kernel : ROLL \ nn..n0 n -- nn-1..n0 nn ; 6.2.2150 DUP>R PICK SP@ DUP CELL+ R> 1+ CELLS MOVE DROP ; : >= S" 1- >" EVALUATE ; IMMEDIATE \ ------------------------------------------------ INCLUDE DSK1.3RD4TH \ ( x y z -- x y z boolean) : NOT_Y<X? OVER 4TH >= ; \ ( x y z -- z ) : ONLY_Z NIP NIP ; \ ( x y z -- x y z x-1 y z) : TAKX 3RD 1- 3RD 3RD ; \ ( x y z result1 -- x y z result1 y-1 z x) : TAKY 3RD 1- 3RD 5 PICK ; \ ( x y z result1 result2 -- result1 result2 (z-1) x y) : TAKZ 2 ROLL 1- 4 ROLL 4 ROLL ; \ ( x y z -- result ) : TAK NOT_Y<X? IF ONLY_Z ELSE TAKX RECURSE TAKY RECURSE TAKZ RECURSE RECURSE THEN ; Edited March 18, 2023 by TheBF typo 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5227418 Share on other sites More sharing options...
+TheBF Posted March 18, 2023 Share Posted March 18, 2023 And some proof that ELSE is overhead. Changing the final definition to this lets the code jump out early if the first test is true. Time is 83.98 seconds : TAK NOT_Y<X? IF ONLY_Z EXIT THEN TAKX RECURSE TAKY RECURSE TAKZ RECURSE RECURSE ; 4 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5227422 Share on other sites More sharing options...
+TheBF Posted March 19, 2023 Share Posted March 19, 2023 I think the rabbithat programmer that translated the LISP TAKEUCHI code was new to Forth. There are two traditional commandments in Forth from the "oral tradition". "Thou shalt not PICK" "Thou shalt never ROLL" So by just removing the sacrilegious ROLL word used in TAKZ the time drops to 62.31 seconds. 38% faster \ ( x y z result1 result2 -- result1 result2 (z-1) x y) \ : TAKZ 2 ROLL 1- 4 ROLL 4 ROLL ; : TAKZ ROT 1- >R 2SWAP R> -ROT ; Then if we only use only standard Forth words and replace the faulty use of 2 PICK with OVER and "OVER SWAP" with NIP, the time is 54.25, 45% faster. And finally if we use the special 3RD and 4TH words available to Camel99 the time drops to 48.48 seconds, 51% or over 2X faster. If we extrapolate these results to the Mecrisp Forth numbers it would be 134/2= 67 milliseconds or 120 times faster than list and about 1/2 speed to C. So that's why it's good to keep the commandments. 2 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5227702 Share on other sites More sharing options...
+TheBF Posted March 19, 2023 Share Posted March 19, 2023 (edited) Down the rabbit hole I go. I found an big error in the Ultimate Forth Benchmark website. The TAK version shown there runs in no time and with 18 12 6 TAK it returns 6. The correct answer is 7. So this final version, removes all the superfluous subroutine calls and uses EXIT to leave as fast a possible. The runtime now is 35.5 seconds. This is almost 3X faster than the original code on my ITC Forth system. (The DTC Forth result for the same code is 29.7 seconds) \ Equivalent Forth definition of Takeuchi function: \ Edited for Camel99 Forth NEEDS ELAPSE FROM DSK1.ELAPSE \ timer : >= S" 1- >" EVALUATE ; IMMEDIATE \ ------------------------------------------------ INCLUDE DSK1.3RD4TH : TAK ( x y z -- result ) OVER 4TH >= IF NIP NIP EXIT THEN 3RD 1- 3RD 3RD RECURSE 3RD 1- 3RD 5 PICK RECURSE ROT 1- >R 2SWAP R> -ROT RECURSE RECURSE ; : TAKTEST 18 12 6 TAK . ; And the things you can find on the internet! AcornUser052-Nov86 : Free Download, Borrow, and Streaming : Internet Archive Here is a page of results on BBC Acorn computers with Z80 and 6502 CPUs. Unfortunately the article does not show the Forth source that they used so we can't do a direct comparison. They mention that the benchmark code was "optimized for speed" The old 99 is in the ballpark. Edited March 19, 2023 by TheBF typo 4 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5227774 Share on other sites More sharing options...
+TheBF Posted June 29, 2023 Share Posted June 29, 2023 I accidently stumbled across this Forth 2020 site and found this 1968 listing of Forth by Chuck Moore. Wonderful to see it there. Forth2020 - Forth 1968Listing There is also a bunch of other neat things on the site for the Forth aficionado, including this interview with Chuck that really gives some insight into how he thinks. Forth2020 - About Forth 4 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5276994 Share on other sites More sharing options...
+TheBF Posted July 5, 2023 Share Posted July 5, 2023 (edited) Since this topic is about Forth "fun" I thought I would share some stuff I am having "fun" with. I wanted to add the "next word/previous word" function to VI99. Years ago I was quite confused/indignant about the new Forth way of handling strings as an (address, length) pair on the data stack. Seemed too complicated managing two items for every string. I was pretty sure that my byte counted strings were good enough for any purpose under the sun. Now I am getting why some senior Forth people changed things. We all have -TRAILING to remove trailing spaces from a string and return the stack string pair that defines the new string. For the editor I took a page from this idea and made: : LASTCHAR ( addr len -- char) 2DUP + 1- C@ ; : -ASCII ( addr len -- addr' len') BEGIN DUP WHILE LASTCHAR BL <> WHILE 1- 0 MAX REPEAT THEN ; With these tools the PREVWORD definition is simple. : PREVWORD -TRAILING -ASCII ; Where -TRAILING removes any "trailing blanks" from the string and then -ASCII scans backwards past anything that is NOT a blank character. In the case of PREVWORD the resulting string length is the same as the screen column so we just need this code to set the editor column. ( addr len) NIP COL ! Although not standard words most modern Forth systems have SKIP and SCAN which search a string from start to end. I think these first showed up in FPC for DOS by Tom Zimmer and used the intel x86 string scanning instructions. They stuck with the community. Like -TRAILING, SKIP and SCAN return a new address, length pair string. This means they can eat their own output. To get a string that starts with the next word we just need this: : NEXTWORD BL SCAN BL SKIP ; Where BL SCAN searches forward for the next occurrence of a "blank" (old term for space character) Then BL SKIP skips past "blank" characters until it finds a non-blank character ie: a word. Getting the editor column value is a bit more complicated because NEXTWORD it is cutting off the front of the string word by word. So we need to subtract the new length from the full length of original line in the editor to compute the COL value. There is a bit more to the final code because at the end of a line we need to go up or go down in the file depending on the direction. And VI editors can be given a numeric argument and so they can go ahead or back any number of words. I will be putting up the code on Github this week so you will just have to wait until then for the full story but here is a peek. : GOFORWARD DUP 0= \ 0 means we are at end of line IF 2DROP GODOWN COL OFF ELINE# SEEKTO END NEXTWORD ; : CMD-w \ next word command ELINE# SEEKTO RIGHTSIDE ARGS ?DO GOFORWARD LOOP NIP SEEK$ @ LEN SWAP - 0 WIDTH CLIP COL ! ; : GOBACK ( addr len -- addr' len') DUP 1 < \ test for beginning of line IF 2DROP \ don't need this string now GOUP \ go up one line ELINE# SEEKTO \ re-seek to the new line DUP COL ! END PREVWORD ; : CMD-b \ previous word command ELINE# SEEKTO LEFTSIDE 1- \ start scan at the cursor-1 ARGS ?DO GOBACK LOOP NIP COL ! ; Edited July 5, 2023 by TheBF typo 2 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5279219 Share on other sites More sharing options...
+TheBF Posted July 9, 2023 Share Posted July 9, 2023 I realized in the NEXTWORD definition above, I forgot to give everyone a version of SKIP and SCAN with the magic word /STRING. These ones work in TurboForth. Now that Lee is adding these strange multiple While statements to FbForth, he can use this code too along with his fancy new REPEAT word. -ROT is a code word but ROT ROT will do the same thing. : /STRING ( a u n -- a+n u-n) \ trim string ROT OVER + -ROT - ; : SCAN ( adr len char -- adr' len') >R \ remember char BEGIN DUP WHILE ( len<>0) OVER C@ R@ <> \ test 1st char WHILE ( R@<>char) 1 /STRING \ cut off 1st char REPEAT THEN R> DROP \ Rdrop char ; And of course SKIP is very similar : SKIP ( adr len char -- adr' len') >R \ remember char BEGIN DUP WHILE ( len<>0) OVER C@ R@ = \ test 1st char WHILE ( R@=char) 1 /STRING \ cut off 1st char REPEAT THEN R> DROP \ Rdrop char ; /STRING should also be a code word. It's waaaay more efficient because the 9900 can reach into the stack memory directly. Here is the my version but it just needs a few tweaks to work in the other systems. CODE /STRING ( c-addr1 u1 n -- c-addr2 u2 ) TOS *SP SUB, TOS 2 (SP) ADD, TOS POP, NEXT, ENDCODE [THEN] 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5281246 Share on other sites More sharing options...
+TheBF Posted July 9, 2023 Share Posted July 9, 2023 I found somebody who says you can learn Forth in just 7 steps. (It took me more) Forth in 7 easy steps • JeeLabs 2 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5281248 Share on other sites More sharing options...
+TheBF Posted July 9, 2023 Share Posted July 9, 2023 And one more thing... You can speed up 1 /STRING with this simple definition. : 1/STRING 1- SWAP 1+ SWAP ; I considered calling it CIRCUMCISE but I thought better of it. 1 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5281253 Share on other sites More sharing options...
+TheBF Posted July 20, 2023 Share Posted July 20, 2023 I got this website via Reddit /Forth that gives a very comprehensive history of Forth. I looks like the writer has recently discovered Forth (created Feb 2023) and wrote up what he has found over the course of the year. It's a good reference site for history and other web links. Forth: The programming language that writes itself: The Web Page (ratfactor.com) 4 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5287140 Share on other sites More sharing options...
Reciprocating Bill Posted July 21, 2023 Share Posted July 21, 2023 On 7/8/2023 at 9:40 PM, TheBF said: I considered calling it CIRCUMCISE but I thought better of it. There's a quip in here somewhere about a forthskin. But you won't hear it from me. 1 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5287325 Share on other sites More sharing options...
+TheBF Posted July 21, 2023 Share Posted July 21, 2023 53 minutes ago, Reciprocating Bill said: There's a quip in here somewhere about a forthskin. But you won't hear it from me. There should be a prize for that that post. (I think I have heard that term on comp.lang.lisp) 1 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5287342 Share on other sites More sharing options...
+TheBF Posted August 3, 2023 Share Posted August 3, 2023 I just saw something on comp.lang.forth that I would have never thought of but apparently others have used it. It is not guaranteed to work on all Forth systems. You need the Forth83 style word CREATE so it doesn't work in FbForth without making that word. Let's say you wanted to create a vector table of these actions that you have written up. : FIZZ ; : BUZZ ; : BLEEP ; : HALT ; In many Forth systems you can do this to make the table of executable "tokens" (addresses) CREATE TABLE ] FIZZ BUZZ BLEEP HALT [ For the Forth student: How does it work? Because ] turns on the compiler so the token for each word is looked up in the dictionary and is compiled into memory as each word is parsed. Then [ turns off the compiler ie: turns on the interpreter again And then to run them you might do something like this. : DOIT ( n -- ) CELLS TABLE + @ EXECUTE ; \ Usage: 0 DOIT 1 DOIT etc. (no protection on this example so you can blow up the system if you type 4 DOIT. ) ( I will leave it to the reader to implement protection ) (always wanted to say that) More stuff you'll probably never need but now you know. 3 Quote Link to comment https://forums.atariage.com/topic/338224-tf-camel-fb-forth-fun/page/6/#findComment-5293143 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.