Ha, I spotted your deliberate mistake! After tracing through this word by word and dumping stack a a million times I see we need a "swap" in the does> like so:
Then I noticed you have "swap" in your first example. Which might have been a clue, duhhh.
Notice my definition of the sol symbol to dump stack. That is the first time I have used that key on any keyboard for anything (What actually is it for anyway?)
Can I understand this simply as: create makes us a new dictionary entry that does nothing, basically empty. Using bx now gets us the address of a zero length memory area. allot attaches memory to that new word, uses "cells" and our given size on the stack to get the length right. Using bx now gets us the address of the allocated memory. does> attaches some code to that dictionary entry. Using bx now gets us the address but also triggers execution of the does> code.
If so why don't they say that in any of the descriptions I have seen? Most of which is unintelligible.
Ha, I spotted your deliberate mistake! After tracing through this word by word and dumping stack a a million times I see we need a "swap" in the does> like so:
Then I noticed you have "swap" in your first example. Which might have been a clue, duhhh.
Notice my definition of the sol symbol to dump stack. That is the first time I have used that key on any keyboard for anything (What actually is it for anyway?)
Can I understand this simply as: create makes us a new dictionary entry that does nothing, basically empty. Using bx now gets us the address of a zero length memory area. allot attaches memory to that new word, uses "cells" and our given size on the stack to get the length right. Using bx now gets us the address of the allocated memory. does> attaches some code to that dictionary entry. Using bx now gets us the address but also triggers execution of the does> code.
If so why don't they say that in any of the descriptions I have seen? Most of which is unintelligible.
Looks like I can make some progress now.
Using the sol symbol are you? You are about to be assimilated, don't fight it.
Heater, good work finding my "deliberate" mistake. Actually, I didn't test the code. Sorry for putting a bug in it. Yes, DOES> looks complicated, but it is really quite simple once you realize how it works. I had a similar problem trying to understand DOES>. There should be an easier way to describe it, but now when I look back at the tutorials they make perfect sense. The problem is that one's mind needs to be molded into the Forth way before it makes sense.
There are two sorts of words ( definitions, functions or what ever).
The first type are helpers. Words such as pinhi, pinlo, |< should be self explanatory. A forth user wiring code for the prop may well have a catalogue of these words, but they are unlikely to be needed for a PC applications.
The array word example, is a word which extends the Forth compiler.
If you want to extend a forth compiler to look more C like then you could probably add pointer words at a stretch.
Why you would want to do such a thing, I don't know.
Forth is a rapid application development environment. A good choice of helpers makes code more readable
That "deliberate" mistake was very fortunate. I knew the idea would work because, well, it was you that wrote it, it just needed debugging. So I start reading about "cells" and "does>" and tryingh to figure out what goes on and where everything meshes on that persky stack. That just gives me headache agiain so it's time to write everyting out word by word,line by line and intersperse .S everywhere. Just watch what happens (That's where the sol came in.) Bingo it's obvious where the stack falls apart. So now I do understand it all. Had it just worked I might have been a bit lazy about really getting a grasp of it.
The problem is that one's mind needs to be molded into the Forth way before it makes sense.
I have a little theory about that.
1) All programming languages are about the same. After all there are only a handful of things a computer can do: sequence, select, iterate, save, recall and some operators. So no matter how the language looks that is all can possibly do.
2) Therefore all Programming languages should all be describable in the same "meta-language" That meta-language in my case is a mix of English and some mathematical notation.
3) BUT English is very bendable, malleable. So we end up with a different meta-language, a different kind of dialect of English, for each programming language. 2) above breaks down.
That means that regardless of any "forth way" or other way one has to get to grips with the meta-language describing the "way" first. Have a look at the languages used to describe C++, Eiffel, Algol, Ada and so on to see what I mean.
And, as you said, once you have been through programming language step by step the meta-language starts to make sense. Kind of chicken and egg thing.
As much as I love C I don't want to make Forth look too much like C. It would only confuse things. Probably end up being nothing like C and poor Forth code anyway.
Forth is a rapid application development environment.
Yeah, I'm having a bit of trouble seeing the "rapid" part of that just now.
As much as I love C I don't want to make Forth look too much like C. It would only confuse things. Probably end up being nothing like C and poor Forth code anyway.
Yeah, I'm having a bit of trouble seeing the "rapid" part of that just now.
I think the main reason that Forth is considered a rapid development environment is that it is interactive. It lets your program in small pieces and build up to a full working program. It seems to me that that is mainly due to its interactivity not the stack paradigm. If there was another interactive language for the Propeller would we still be talking about Forth so much? We have FemtoBasic but it is way too slow to do anything serious. Besides, it doesn't really even support procedural abstraction so it's hard to build up a set of reusable functions. Maybe what is needed is a simple but powerful infix language that runs on the Propeller and is interactive. What would be a good candidate? A more modern version of Basic with named functions with paramenters instead of GOSUB? One thing that's hard to beat in Forth is what used to be the "CREATE ... DOES> ..." pattern. That or something like it lets you extend the syntax of the language in a way that isn't possible in Basic or even C. Is there another language that offers a similar capability? I guess Lisp does but I think we'd find fewer people willing to use Lisp than Forth and it probably isn't well suited to a microcontroller like the Propeller anyway.
Can I understand this simply as: create makes us a new dictionary entry that does nothing, basically empty. Using bx now gets us the address of a zero length memory area. allot attaches memory to that new word, uses "cells" and our given size on the stack to get the length right. Using bx now gets us the address of the allocated memory. does> attaches some code to that dictionary entry. Using bx now gets us the address but also triggers execution of the does> code.
If so why don't they say that in any of the descriptions I have seen? Most of which is unintelligible.
Just TELL you? What fun would that be?
Actually, DPANS94 describe the FUNCTION for each word, the end usage can have different implication depeniding on design decisions. For example CREATE: http://lars.nocrew.org/dpans/dpans6.htm#6.1.1000
6.1.1000 CREATE
CORE
( "<spaces>name" -- )
Skip leading space delimiters. Parse name delimited by a space. Create a definition for name with the execution semantics defined below. If the data-space pointer is not aligned, reserve enough data space to align it. The new data-space pointer defines name's data field. CREATE does not allocate data space in name's data field.
name Execution: ( -- a-addr )
a-addr is the address of name's data field. The execution semantics of name may be extended by using DOES>.
says that we use CREATE to start a definition. It also specifies that it returns the identifiers data field. In propforth, Sal decided the datafield was unneeded on his threading model. This saves one address for every definition. The consequence is that the CREATE ... DOES> construct doesn't work as expected. So even though propforth's CREATE does not behave the same way as many other forths, it is still compliant with the standard. The design decision was that we would specify a separate word for the compile time behavior and a separate word for the execution time behavior. This is probably the biggest issue with code portability and propforth. This is a rather small impact, since in code the defining words usually occur in the front and are easy to find and fix, and on the prop there are should zero to three (non statistical guestimate) non-kernel defining words anyway, due to space limits.
The intent is to allow maximum flexibility to get the most out of the resources available.
I think the main reason that Forth is considered a rapid development environment is that it is interactive.
This is the biggie. We can eliminate most of the steps that require running the PropTool, and re-loading to EEPROM. WE can to lots and lots of small quick tests and thoroughly test each word via the command line before stringing them into larger functions. Usually any error will be manifest very quickly, as it is very difficult to design and test a word and still have it incorrect (although I have seen very clever people that can make ANYTHING into an error, but they tend to share this skill in all aspects of life, not just coding).
Once we get the hang of command line interaction to thoroughly test each word at the time it is being developed, we can realize a huge increase in productivity. We don't have to go back and debug typos and other "simple $h1t" would otherwise would cost multiples of the development time. We get to concentrate on the engineering, and not be plagued by dumb stuff. If time for developing in assembler is the baseline, using a high level language usually sees a 1 to 10 times reduction. Folks report a similar 1x to 10x reduction on top of this using forth. Whether you see a 1x or 10x (or some other) reduction is largely dependent on the user's skills and abilities. Expect it to take long than a week to start seeing this affect.
I think the main reason that Forth is considered a rapid development environment is that it is interactive.
I must be missing a point here somewhere. So far I don't get any benefit out of that interactivity.
I kicked off with two very simple functions that can be expressed in a handful of lines of code in almost any programming language including assembler. Trying to get those working interactively just did not work for me. Better to write the whole thing out, work through it in your head keeping track of where everything is on paper or by adding stack comments to the code. Then running the thing as a whole "gforth filename.fs". When it does not work a few goes with ".s" here and there to see what happens and you are soon in business. Even so the finished code is "write only" it's really hard to see the algorithm buried in all those stack ops and with no variable names.
All in all nothing more rapid than writing in any other language and then checking things out with print statements or stepping through with a debugger.
Sorry, I just can't keep this stuff straight in my head when just typing into the Forth prompt.
Maybe what is needed is a simple but powerful infix language that runs on the Propeller and is interactive.
Isn't the problem that a compiler/interpreter engine for any "normal" language is much more complex and bigger? Forth has a minimal syntax and not much in the way of semantics so it's very small and fits in devices like the Prop. The clever part is in being able to use Forth to extend Forth in some way.
Just now I'm not seeing the advantage of developing on the target itself if the cost in terms of effort and readability is so high.
Whilst I now have a little handle on "CREATE ... DOES> ...", I don't see that is actually extending the syntax as such. I guess on could write a compiler for some other syntax, say infix expressions, in Forth and then claim that you have extended the Forth syntax. I really don't think that's what people mean though.
This is the biggie. We can eliminate most of the steps that require running the PropTool, and re-loading to EEPROM. WE can to lots and lots of small quick tests and thoroughly test each word via the command line before stringing them into larger functions. Usually any error will be manifest very quickly, as it is very difficult to design and test a word and still have it incorrect (although I have seen very clever people that can make ANYTHING into an error, but they tend to share this skill in all aspects of life, not just coding).
Once we get the hang of command line interaction to thoroughly test each word at the time it is being developed, we can realize a huge increase in productivity. We don't have to go back and debug typos and other "simple $h1t" would otherwise would cost multiples of the development time. We get to concentrate on the engineering, and not be plagued by dumb stuff. If time for developing in assembler is the baseline, using a high level language usually sees a 1 to 10 times reduction. Folks report a similar 1x to 10x reduction on top of this using forth. Whether you see a 1x or 10x (or some other) reduction is largely dependent on the user's skills and abilities. Expect it to take long than a week to start seeing this affect.
I agree with all of that but I'm not sure it has a lot to do with the Forth language itself. It seems like the same could be accomplished with any interactive language.
Oh ohh! You are telling me that "CREATE ... DOES>" is not working, or at least works differently, accross all Propeller Forths. As you may have guessed by now I'm searching for the "real Forth" or that subset of it that will work accross all Propeller Forths and hopefully other forths too, at least gforth. That means "CREATE ... DOES>" is off the table for this exercise.
I did not understand your statement that your "CREATE...DOES>" is still compliant with the standard." When you also say that "It [the standard] also specifies that it returns the identifiers data field." BUT that your Forth does not do that. This is contradictory.
This is the biggie. We can eliminate most of the steps that require running the PropTool, and re-loading to EEPROM.
I can appreciate that, but it actually just moves work from the tools to me.
WE can to lots and lots of small quick tests and thoroughly test each word via the command line..
I absolutely could not do that with those two functions we have worked through so far. I had to write them out in detail. Understand what is going on, and then it works. Much like any other language.
I did not understand your statement that your "CREATE...DOES>" is still compliant with the standard." When you also say that "It [the standard] also specifies that it returns the identifiers data field." BUT that your Forth does not do that. This is contradictory.
Slow down a bit. The execution time behavior is to return the word's datafield address. Propforth doesn't have a datafield address, so it doesn't have anything to return.
but it actually just moves work from the tools to me.
Yes. Forth does not rely on a powerful PC running a complex suite of software to support development. All we need is a simple terminal program to get to the command line, DOS PC terminal program will work. This is handy when all we have is a terminal program on say my android phone, and the target is in another city. Or in orbit.
Of course if you have the target right on your desk wired to a massive workstation, you have other options.
I absolutely could not do that with those two functions we have worked through so far. I had to write them out in detail. Understand what is going on, and then it works. Much like any other language.
I'm not sure what you couldn't do. We can easily do the compile time behavior of create by defining a variable ArrayStart, and and allot'ing bytes after it. We can easily define a word @Array that takes and offset and returns a value, and !Array that take value and an offset and stores the value at that offset. In fact this is how its done. Either in a single complex definition that uses CREATE ... DOES> or two or more separate definitions, both methods are equivalent.
You are correct, we have to understand what we want in the first place, in order to get it.
Slow down a bit,
"The execution time behavior is to return the word's datafield address" - From the spec I guess.
"Propforth doesn't have a datafield address, so it doesn't have anything to return." - That means it does not return an address of the word's data field. That means it breaks the standard as spelled out in the first statement.
What actually does it return?
How do I create arrays and nice accessors in PropForth that also work in other Forths?
Never mind the power of the PC. Yes, an interactive environment saves all that waiting for the compilation to complete, and downloading to RAM/ROM. But it replaces it with all that typing and retyping of experiments as you describe.
I'm pretty sure no one is developing code directly on orbiting vehicles as you suggest.
I'm not sure what you couldn't do...
I was referring to the bitreverse and sqrti functions. Such simple things made so hard by not having variable names and only a stack to juggle. Perhaps it's me but keeping that stack juggling in mind whilst typing interactively does not come naturally.
...we have to understand what we want in the first place, in order to get it.
In the case of sqrti and bitreverse I know exactly what I want before I start. Just wondering why it's so hard to get it:)
That means it does not return an address of the word's data field. That means it breaks the standard as spelled out in the first statement. What actually does it return?
True. True. It return Nothing, there is no datafield address, so it does not return anything. We cannot use the same CREATE .. DOES> construct in propforth, as it does not work that way.
How do I create arrays and nice accessors in PropForth that also work in other Forths?
If you use the equivalent two part method described previously, the code will work on any forth that supports defining variables. (pretty much all to them do, the ones that don't are not used for the same purpose).
But it replaces it with all that typing and retyping of experiments as you describe.
We have to do the testing anyway, in all cases, or risk issuing untested and probably buggy software. At some point we have to stop testing and ship, but forth makes it easier to do more testing rather than easier to want to skip testing.
I'm pretty sure no one is developing code directly on orbiting vehicles as you suggest.
All the space probes that need code modification years of decades after they are launched do. This is why forth is the fequent choice for space applications.
I was referring to the bitreverse and sqrti functions. .... wondering why it's so hard to get it:)
As I mentioned, you are doing it the hard way by trying to swallow the elephant in one byte. This is why I say your previous code habits are in the way. Try your code worrd by word, and look at the stack before and after each word. Its very easy to keo track of what the two or three values on the stack are, and to see the effect each word has on them. You know what you want, do one step at a time. This is the easy way. If you choose write a big long page of code first, then try to muddle through it, you may find this to be significantly more difficult.
Are you saying I can use Dave's array example without problems? The one without the DOES>.
Yes, we have to do the testing. Lord knows I've done my share of testing in aerospace flight controls and elsewhere, in the minutest detail. Not sure how the interactive environment helps with that.
All the space probes that need code modification years of decades after they are launched do.
I'm pretty sure they are not hacking on a terminal up link do that. Given that time delays involved that might not be so much fun. Any code up loaded will have been thoroughly exercised on ground first.
This is why forth is the fequent choice for space applications.
You can upload code written in any language. That is no reason. Any links to articles/stories about Forth in space?
As I mentioned, you are doing it the hard way by trying to swallow the elephant in one byte
As I said, those functions are no elephants. They are very small and self contained. More like an ant.
Anyway, I'm still at it...time to stop all this philosophical debate and get on with the practical doing it part.
Here are some of the satellites and other systems that use Forth.
Small Payload Accomodations Interface Module (SPAIM) (RTX2010 RAD Hard Forth Processor)
Advanced Composition Explorer (ACE) (RTX2010)
Near Earth Asteroid Rendezvous - Shoemaker (RTX2010)
TIMED (Thermosphere Ionosphere Mesosphere Energetics and Dynamics)
Space Shuttle Robot Arm Simulator
IMS there are at least a dozen other satellites that use Forth. NASA used to have a web page devoted to Forth but its gone.
If you want to check out more modern Forth's check out Forth Inc's software Swift cross-compilers for embedded systems.
Are you saying I can use Dave's array example without problems?
Of course you can use Dave's code. "Problems" is up to you. I used code from a different forth in the example I post, it deviates from the standard in a different direction than propforth. The effort was about the same as looking at my own code from a year ago.
This is an example of how forth is portable across flavors, and was completely simple (or I wouldn't have bothered). It also shows something about breaking down the functions and testing interactively word by word. Try this or don't, I advise that this is the easy way, and whatever this is, it is different from what you are doing, and that is why you are having trouble.
I'm pretty sure they are not hacking on a terminal up link do that.
Good guess. They also are not plugging in a JTAG plug and emulator from the workstation either, unless they have VERY long cables.
You can upload code written in any language. That is no reason. Any links to articles/stories about Forth in space?
Google is your friend. Exclude "back and forth" and the search is more productive.
As I said, those functions are no elephants. They are very small and self contained. More like an ant.
I can't argue the same point too much longer. All I will say is "stop fighting your tools". You have found that pounding screws with a hammer is ok but awkward, and that driving nails with a screwdriver is difficult. Now try hammer + nails and screwdriver + screws. Thats all I can offer, you have to give yourself enough time to get familiar with the tools, till it clicks.
I must be missing a point here somewhere. So far I don't get any benefit out of that interactivity.
.
.
.
.
Well if you had stuff to interact with beside FFT such as a new nichromium heat element I'm try to dial in interactively you would see the benifits. Or trying to set the min max settings of an explosion proof pump under actual load with interactive 8 channel PWM. Or getting a new chip/sensor to do something/anything useful without having to edit, recompile, load, execute, wash, rinse, repeat. It's not about what FORTH is, it's about what you can do with a good FORTH system (software / hardware) in the real world.
Thanks for the project list. I'll have a search around.
I'm afraid Forth Inc's Swift is of no interest to me. Not that it may be anything other than excellent. I have seen so many projects bitten by the problems with closed source, proprietary, non-tandard languages, operating systems and tools. Starting with Intel and PL/M back in the early days. All of a sudden huge amounts of code can become un-maintainable and unusable because the vendor drops the the thing or disappears altogether. I don't want to do that again and neither does the modern embedded world.
No, the name of the game for this exploration is Propeller Forths. And gforth because that is what I have.
Of course you can use Dave's code. "Problems" is up to you.
When I said can I use it without problems I meant "will that code as written work as expected without further change?" That is not up to me but up to the various Propeller Forth implementations.
Of course using the lowest common denominator might lead to complications down the line, so be it.
Heater, I suggest you implement your FFT on gForth first, and then worry about how you will implement it on pfth, PropForth and Tachyon. gForth on a PC gives you a much better development environment than you will get under the Prop Forths. It has been said many times that it is efficient to develop in Forth because of the interactive interpreter. But I think that claim is false. One problem is that Forth source is compiled into execution code, so there is no way to save your source after you have written it under the interpreter. Of course, there are certain tools that have been developed to get around this, such as Forth disassemblers, but that just complicates things.
To be honest, the requirement of having to do code-and-test on small pieces of Forth code is really not good coding practice. In my opinion, it's much better to write code in an editor and test it only after you have written complete functions instead of small pieces of a function. The practice of breaking functions up into ridiculously small Forth snippets is used because it becomes difficult to track more than 3 or 4 things on the stack at one time.
Once you have your code working under gForth it should port to pfth pretty easily. Porting to the other two Forths will require that you write words that are missing, such as r@ in PropForth. You might be able to implement DOES> in PropForth and Tachyon, but it probably isn't worth the effort. So you should avoid using DOES>.
That's exactly what is happening. If only because over the weekend I had no Propellers near by, only a laptop and gforth. So I write my code into a file and run it. How fast could a development cycle be?
To be honest, the requirement of having to do code-and-test on small pieces of Forth code is really not good coding practice. In my opinion, it's much better to write code in an editor and test it only after you have written complete functions instead of small pieces of a function.
I can only agree. I have never known a serious serious software producer, say in aerospace, that would tolerate a develpment process like that. The interactive thing is cool when you know absolutely nothing about the language and just like to try a quick experiment of a few lines. Much like you might do in Python for example.
The practice of breaking functions up into ridiculously small Forth snippets is used because it becomes difficult to track more than 3 or 4 things on the stack at one time.
It's nuts. Basically:
1) You don't want to use global variables, it's bad practice,and discouraged by everyone including Forthers.
2) You don't want to use local variables, some Forths don't have them and they are discouraged by Forthers, especially Chuck himself.
3) That means stack juggling.
4) That means lots of small snippets.
But wait, now you have lots of names of useless Forth snippets which are polluting your global name space. You might as well have gone with global variables in the first place.
Braino, keeps talking about elephants. The Elephant here is that big buterflies function with three nested loops and lots of global variables. Just now I cannot begin to imagine how to approach that beast in Forth.
IMO the advantage of writing small bits of code first is that I find it enables us to visualize the problem better leading to a better solution, a better way of tackling the problem resulting in a simpler solution = better solution. Incremental programming also allows those functions to be tested and proven too which means easier debugging of the completed code. If debugging is the art of removing bugs from a program then programming must be the art of putting them in. Let's avoid that.
In regards to lots of snippets I don't think there is a problem with that as long as those snippets were snipped thoughtfully and well labeled. Too many times I have seen lots and lots of snippets that might only be 2 or 3 words and yet the name of the snippet was longer and harder to remember. Using long descriptive names is a practice inherited from conventional compiler languages and are ill suited for interactive testing for starters. What's needed is a good thesaurus (in your head) and to stop constraining your thinking to conventional language syntax etc. How many times have I seen a word that may be named turn_on_red_led when in Forth I just say ON RED LED. Which is better? When I can just as easily say OFF BLUE LED or ON HOT WATER without having a special word for it then it makes a lot of sense.
As for stack juggling, if you are doing that then you may as well join a circus, at least you'll get paid for juggling. I detest the PICK and ROLL words, they are a poison and you never need to use them, I don't, and yet they are ANS, what does that say?. There is an advantage to the judicious use of local or global variables and I will use variables when it makes sense to. The rest of the time some simple stack manipulation is all that is needed.
This is probably more than I wanted to say but it's either food for though or fuel for the fire, take your pick.
...programming must be the art of putting them[bugs] in.
I like that:)
...food for though or fuel for the fire.
I'll take it as food for though. This is a challenge I have set myself, any advice is welcome. I'll leave my thoughts about short global function and variable names for another day.
...you may as well join a circus...
Ha ha, problem is I'm no good at juggling:) I must admit that pick and roll do seem to be a measure of last resort.
Now to the problem at hand...
Just now I was looking at the butterfly loops of the fft and wondering how on earth I can tackle that monster. It has a pile of local variables. It occurred to me that those local variables are not really part of the actual problem that this code solves. They are used as scratch pad space and generally to make the calculation easier to read. So, I can get rid of most/all of them by merging the expressions into bigger expressions. For example this code:
b0 := flightIndex + butterfly
b1 := b0 + flightSize
// ...the butterfly.
a := bx[b1] // Get X[b1]
b := by[b1]
c := wx[wIndex]
d := wy[wIndex]
k1 := (a * (c + d)) >> 12
k2 := (d * (a + b)) >> 12
k3 := (c * (b - a)) >> 12
tx := k1 - k2
ty := k1 + k3
k1 = bx[b0]
k2 = by[b0]
bx[b1] = k1 - tx
by[b1] = k2 - ty
bx[b0] = k1 + tx
by[b0] = k2 + ty
Using short names as suggested makes it fit on the page at least!
So we have removed 9 local variables at the cost making it impossible to read. Never mind:)
Given that my arrays are global anyway I can evaluate the right side of those expressions in Forth using only the Forth stack and perhaps only a and b as globals (or can I hide them on the return stack?)
I still have those pesky array indices fi, bf, fs, w to worry about they are basically the parameters to this block.
Of course we can see a bunch of sub-expressions there that can be factored out into their own words.
So Peter, or any Forther's, is this a reasonable way to proceed? Any suggestions?
... "will that code as written work as expected without further change?" ...
Yes, I understand that. No, it it most likely that any code you get from any source will need at least some change on any other platform or environment, same as with any other language. And you can insist that a language such as C is always completely portable from one compiler to the next, but I do not share that opinion as I have already cited my example that isn't.
I will also again advise you that in my experience, per the examples I have given in this thread, that taking forth code from another source and porting it to at least propforth is about equivalent to reading my own from six months prior and using on today's task. That is, getting the two square root functions to work on propforth was about as difficult as looking at the ANSI support extension and getting it to run on a new version of propforth (Last time it was edited I change the numeric literals to h_ and d_ prefixes, instead of having to keep track of the current cog's BASE).
To be honest, the requirement of having to do code-and-test on small pieces of Forth code is really not good coding practice. In my opinion, it's much better to write code in an editor and test it only after you have written complete functions instead of small pieces of a function. The practice of breaking functions up into ridiculously small Forth snippets is used because it becomes difficult to track more than 3 or 4 things on the stack at one time.
This is NOT a requirement. This is a recommendation as a behavior of the most successful people, after decade of experience. Think about it. If you write one million lines of code before you test, you will probably have more than a million errors in it. AND every error will impact the rest of the code, so it will be impossible to to determine any change fixes something or breaks something else. Writing one line will probably have a few errors, but the effects of each error is easy to identify and fix.
You don't have to do it this way, but I used to program that way (write the whole thing before testing), and in my opinion "small bits" is much easier and faster. Which is why folks use forth. One CANNOT do this so easily in other environment, hence the appeal of forth.
It is your opinion and do anything anyway you want, but this NOT breaking stuff up into small easily solvable pieces is usually defined as a "bad habit". Yes it shows you are smarter than me, but makes significantly more work in the long run, particularly for a beginner, with heater as an example. .
b0 := flightIndex + butterfly
b1 := b0 + flightSize
// ...the butterfly.
a := bx[b1] // Get X[b1]
b := by[b1]
c := wx[wIndex]
d := wy[wIndex]
k1 := (a * (c + d)) >> 12
k2 := (d * (a + b)) >> 12
k3 := (c * (b - a)) >> 12
tx := k1 - k2
ty := k1 + k3
k1 = bx[b0]
k2 = by[b0]
bx[b1] = k1 - tx
by[b1] = k2 - ty
bx[b0] = k1 + tx
by[b0] = k2 + ty
From the original, I don't know if the second is corret:
You want arrays, bx, by, bz,wy, wx.
You want support functins to store to each array, example using by:
[code]
by! (value offset - by! )
by@ (offest by@ - value)
[code]
You want functions k1, k2, k3, tx, ty.
But that is all I can guess, I I never figured out the FFT stuff.
prof_braino, you really need to study other languages. You will realize that a lot of what the programmer has to do to program in Forth is handled by the compiler in other languages. In most other languages it's not necessary to keep track of the locations of variables on the stack. It's more efficient to program in other languages because of this. Other languages also have syntax checking and other error checking features, which makes it easier to catch mistakes before executing the code. This allows one to translate algorithms directly into code. It's difficult to do the same thing in Forth.
The C code that Heater has for the FFT is almost a one-to-one translation of the algorithm as written in mathematical notation. If we use global variables we should be able to translate this to Forth on a line-by-line basis. However, it won't be very efficient if we do it this way. Other languages have optimizing compilers that will generate efficient assembly, and there is no need to re-organize the code.
Comments
Ha, I spotted your deliberate mistake! After tracing through this word by word and dumping stack a a million times I see we need a "swap" in the does> like so: Then I noticed you have "swap" in your first example. Which might have been a clue, duhhh.
Notice my definition of the sol symbol to dump stack. That is the first time I have used that key on any keyboard for anything (What actually is it for anyway?)
Can I understand this simply as:
create makes us a new dictionary entry that does nothing, basically empty. Using bx now gets us the address of a zero length memory area.
allot attaches memory to that new word, uses "cells" and our given size on the stack to get the length right. Using bx now gets us the address of the allocated memory.
does> attaches some code to that dictionary entry. Using bx now gets us the address but also triggers execution of the does> code.
If so why don't they say that in any of the descriptions I have seen? Most of which is unintelligible.
Looks like I can make some progress now.
Using the sol symbol are you? You are about to be assimilated, don't fight it.
The first type are helpers. Words such as pinhi, pinlo, |< should be self explanatory. A forth user wiring code for the prop may well have a catalogue of these words, but they are unlikely to be needed for a PC applications.
The array word example, is a word which extends the Forth compiler.
If you want to extend a forth compiler to look more C like then you could probably add pointer words at a stretch.
Why you would want to do such a thing, I don't know.
Forth is a rapid application development environment. A good choice of helpers makes code more readable
That "deliberate" mistake was very fortunate. I knew the idea would work because, well, it was you that wrote it, it just needed debugging. So I start reading about "cells" and "does>" and tryingh to figure out what goes on and where everything meshes on that persky stack. That just gives me headache agiain so it's time to write everyting out word by word,line by line and intersperse .S everywhere. Just watch what happens (That's where the sol came in.) Bingo it's obvious where the stack falls apart. So now I do understand it all. Had it just worked I might have been a bit lazy about really getting a grasp of it. I have a little theory about that.
1) All programming languages are about the same. After all there are only a handful of things a computer can do: sequence, select, iterate, save, recall and some operators. So no matter how the language looks that is all can possibly do.
2) Therefore all Programming languages should all be describable in the same "meta-language" That meta-language in my case is a mix of English and some mathematical notation.
3) BUT English is very bendable, malleable. So we end up with a different meta-language, a different kind of dialect of English, for each programming language. 2) above breaks down.
That means that regardless of any "forth way" or other way one has to get to grips with the meta-language describing the "way" first. Have a look at the languages used to describe C++, Eiffel, Algol, Ada and so on to see what I mean.
And, as you said, once you have been through programming language step by step the meta-language starts to make sense. Kind of chicken and egg thing.
Thanks.
As much as I love C I don't want to make Forth look too much like C. It would only confuse things. Probably end up being nothing like C and poor Forth code anyway. Yeah, I'm having a bit of trouble seeing the "rapid" part of that just now.
Just TELL you? What fun would that be?
Actually, DPANS94 describe the FUNCTION for each word, the end usage can have different implication depeniding on design decisions. For example CREATE:
http://lars.nocrew.org/dpans/dpans6.htm#6.1.1000
says that we use CREATE to start a definition. It also specifies that it returns the identifiers data field. In propforth, Sal decided the datafield was unneeded on his threading model. This saves one address for every definition. The consequence is that the CREATE ... DOES> construct doesn't work as expected. So even though propforth's CREATE does not behave the same way as many other forths, it is still compliant with the standard. The design decision was that we would specify a separate word for the compile time behavior and a separate word for the execution time behavior. This is probably the biggest issue with code portability and propforth. This is a rather small impact, since in code the defining words usually occur in the front and are easy to find and fix, and on the prop there are should zero to three (non statistical guestimate) non-kernel defining words anyway, due to space limits.
The intent is to allow maximum flexibility to get the most out of the resources available.
This is the biggie. We can eliminate most of the steps that require running the PropTool, and re-loading to EEPROM. WE can to lots and lots of small quick tests and thoroughly test each word via the command line before stringing them into larger functions. Usually any error will be manifest very quickly, as it is very difficult to design and test a word and still have it incorrect (although I have seen very clever people that can make ANYTHING into an error, but they tend to share this skill in all aspects of life, not just coding).
Once we get the hang of command line interaction to thoroughly test each word at the time it is being developed, we can realize a huge increase in productivity. We don't have to go back and debug typos and other "simple $h1t" would otherwise would cost multiples of the development time. We get to concentrate on the engineering, and not be plagued by dumb stuff. If time for developing in assembler is the baseline, using a high level language usually sees a 1 to 10 times reduction. Folks report a similar 1x to 10x reduction on top of this using forth. Whether you see a 1x or 10x (or some other) reduction is largely dependent on the user's skills and abilities. Expect it to take long than a week to start seeing this affect.
I kicked off with two very simple functions that can be expressed in a handful of lines of code in almost any programming language including assembler. Trying to get those working interactively just did not work for me. Better to write the whole thing out, work through it in your head keeping track of where everything is on paper or by adding stack comments to the code. Then running the thing as a whole "gforth filename.fs". When it does not work a few goes with ".s" here and there to see what happens and you are soon in business. Even so the finished code is "write only" it's really hard to see the algorithm buried in all those stack ops and with no variable names.
All in all nothing more rapid than writing in any other language and then checking things out with print statements or stepping through with a debugger.
Sorry, I just can't keep this stuff straight in my head when just typing into the Forth prompt. Isn't the problem that a compiler/interpreter engine for any "normal" language is much more complex and bigger? Forth has a minimal syntax and not much in the way of semantics so it's very small and fits in devices like the Prop. The clever part is in being able to use Forth to extend Forth in some way.
Just now I'm not seeing the advantage of developing on the target itself if the cost in terms of effort and readability is so high.
Whilst I now have a little handle on "CREATE ... DOES> ...", I don't see that is actually extending the syntax as such. I guess on could write a compiler for some other syntax, say infix expressions, in Forth and then claim that you have extended the Forth syntax. I really don't think that's what people mean though.
Oh ohh! You are telling me that "CREATE ... DOES>" is not working, or at least works differently, accross all Propeller Forths. As you may have guessed by now I'm searching for the "real Forth" or that subset of it that will work accross all Propeller Forths and hopefully other forths too, at least gforth. That means "CREATE ... DOES>" is off the table for this exercise.
I did not understand your statement that your "CREATE...DOES>" is still compliant with the standard." When you also say that "It [the standard] also specifies that it returns the identifiers data field." BUT that your Forth does not do that. This is contradictory. I can appreciate that, but it actually just moves work from the tools to me. I absolutely could not do that with those two functions we have worked through so far. I had to write them out in detail. Understand what is going on, and then it works. Much like any other language.
Slow down a bit. The execution time behavior is to return the word's datafield address. Propforth doesn't have a datafield address, so it doesn't have anything to return.
Yes. Forth does not rely on a powerful PC running a complex suite of software to support development. All we need is a simple terminal program to get to the command line, DOS PC terminal program will work. This is handy when all we have is a terminal program on say my android phone, and the target is in another city. Or in orbit.
Of course if you have the target right on your desk wired to a massive workstation, you have other options.
I'm not sure what you couldn't do. We can easily do the compile time behavior of create by defining a variable ArrayStart, and and allot'ing bytes after it. We can easily define a word @Array that takes and offset and returns a value, and !Array that take value and an offset and stores the value at that offset. In fact this is how its done. Either in a single complex definition that uses CREATE ... DOES> or two or more separate definitions, both methods are equivalent.
You are correct, we have to understand what we want in the first place, in order to get it.
Slow down a bit,
"The execution time behavior is to return the word's datafield address" - From the spec I guess.
"Propforth doesn't have a datafield address, so it doesn't have anything to return." - That means it does not return an address of the word's data field. That means it breaks the standard as spelled out in the first statement.
What actually does it return?
How do I create arrays and nice accessors in PropForth that also work in other Forths?
Never mind the power of the PC. Yes, an interactive environment saves all that waiting for the compilation to complete, and downloading to RAM/ROM. But it replaces it with all that typing and retyping of experiments as you describe.
I'm pretty sure no one is developing code directly on orbiting vehicles as you suggest. I was referring to the bitreverse and sqrti functions. Such simple things made so hard by not having variable names and only a stack to juggle. Perhaps it's me but keeping that stack juggling in mind whilst typing interactively does not come naturally. In the case of sqrti and bitreverse I know exactly what I want before I start. Just wondering why it's so hard to get it:)
True. True. It return Nothing, there is no datafield address, so it does not return anything. We cannot use the same CREATE .. DOES> construct in propforth, as it does not work that way.
If you use the equivalent two part method described previously, the code will work on any forth that supports defining variables. (pretty much all to them do, the ones that don't are not used for the same purpose).
We have to do the testing anyway, in all cases, or risk issuing untested and probably buggy software. At some point we have to stop testing and ship, but forth makes it easier to do more testing rather than easier to want to skip testing.
All the space probes that need code modification years of decades after they are launched do. This is why forth is the fequent choice for space applications.
As I mentioned, you are doing it the hard way by trying to swallow the elephant in one byte. This is why I say your previous code habits are in the way. Try your code worrd by word, and look at the stack before and after each word. Its very easy to keo track of what the two or three values on the stack are, and to see the effect each word has on them. You know what you want, do one step at a time. This is the easy way. If you choose write a big long page of code first, then try to muddle through it, you may find this to be significantly more difficult.
OK "CREATE...DOES>" is off the table.
Are you saying I can use Dave's array example without problems? The one without the DOES>.
Yes, we have to do the testing. Lord knows I've done my share of testing in aerospace flight controls and elsewhere, in the minutest detail. Not sure how the interactive environment helps with that. I'm pretty sure they are not hacking on a terminal up link do that. Given that time delays involved that might not be so much fun. Any code up loaded will have been thoroughly exercised on ground first. You can upload code written in any language. That is no reason. Any links to articles/stories about Forth in space? As I said, those functions are no elephants. They are very small and self contained. More like an ant.
Anyway, I'm still at it...time to stop all this philosophical debate and get on with the practical doing it part.
Small Payload Accomodations Interface Module (SPAIM) (RTX2010 RAD Hard Forth Processor)
Advanced Composition Explorer (ACE) (RTX2010)
Near Earth Asteroid Rendezvous - Shoemaker (RTX2010)
TIMED (Thermosphere Ionosphere Mesosphere Energetics and Dynamics)
Space Shuttle Robot Arm Simulator
IMS there are at least a dozen other satellites that use Forth. NASA used to have a web page devoted to Forth but its gone.
If you want to check out more modern Forth's check out Forth Inc's software Swift cross-compilers for embedded systems.
Of course you can use Dave's code. "Problems" is up to you. I used code from a different forth in the example I post, it deviates from the standard in a different direction than propforth. The effort was about the same as looking at my own code from a year ago.
This is an example of how forth is portable across flavors, and was completely simple (or I wouldn't have bothered). It also shows something about breaking down the functions and testing interactively word by word. Try this or don't, I advise that this is the easy way, and whatever this is, it is different from what you are doing, and that is why you are having trouble.
Good guess. They also are not plugging in a JTAG plug and emulator from the workstation either, unless they have VERY long cables.
Google is your friend. Exclude "back and forth" and the search is more productive.
I can't argue the same point too much longer. All I will say is "stop fighting your tools". You have found that pounding screws with a hammer is ok but awkward, and that driving nails with a screwdriver is difficult. Now try hammer + nails and screwdriver + screws. Thats all I can offer, you have to give yourself enough time to get familiar with the tools, till it clicks.
Well if you had stuff to interact with beside FFT such as a new nichromium heat element I'm try to dial in interactively you would see the benifits. Or trying to set the min max settings of an explosion proof pump under actual load with interactive 8 channel PWM. Or getting a new chip/sensor to do something/anything useful without having to edit, recompile, load, execute, wash, rinse, repeat. It's not about what FORTH is, it's about what you can do with a good FORTH system (software / hardware) in the real world.
Thanks for the project list. I'll have a search around.
I'm afraid Forth Inc's Swift is of no interest to me. Not that it may be anything other than excellent. I have seen so many projects bitten by the problems with closed source, proprietary, non-tandard languages, operating systems and tools. Starting with Intel and PL/M back in the early days. All of a sudden huge amounts of code can become un-maintainable and unusable because the vendor drops the the thing or disappears altogether. I don't want to do that again and neither does the modern embedded world.
No, the name of the game for this exploration is Propeller Forths. And gforth because that is what I have.
Of course using the lowest common denominator might lead to complications down the line, so be it.
To be honest, the requirement of having to do code-and-test on small pieces of Forth code is really not good coding practice. In my opinion, it's much better to write code in an editor and test it only after you have written complete functions instead of small pieces of a function. The practice of breaking functions up into ridiculously small Forth snippets is used because it becomes difficult to track more than 3 or 4 things on the stack at one time.
Once you have your code working under gForth it should port to pfth pretty easily. Porting to the other two Forths will require that you write words that are missing, such as r@ in PropForth. You might be able to implement DOES> in PropForth and Tachyon, but it probably isn't worth the effort. So you should avoid using DOES>.
That's exactly what is happening. If only because over the weekend I had no Propellers near by, only a laptop and gforth. So I write my code into a file and run it. How fast could a development cycle be? I can only agree. I have never known a serious serious software producer, say in aerospace, that would tolerate a develpment process like that. The interactive thing is cool when you know absolutely nothing about the language and just like to try a quick experiment of a few lines. Much like you might do in Python for example. It's nuts. Basically:
1) You don't want to use global variables, it's bad practice,and discouraged by everyone including Forthers.
2) You don't want to use local variables, some Forths don't have them and they are discouraged by Forthers, especially Chuck himself.
3) That means stack juggling.
4) That means lots of small snippets.
But wait, now you have lots of names of useless Forth snippets which are polluting your global name space. You might as well have gone with global variables in the first place.
Braino, keeps talking about elephants. The Elephant here is that big buterflies function with three nested loops and lots of global variables. Just now I cannot begin to imagine how to approach that beast in Forth.
In regards to lots of snippets I don't think there is a problem with that as long as those snippets were snipped thoughtfully and well labeled. Too many times I have seen lots and lots of snippets that might only be 2 or 3 words and yet the name of the snippet was longer and harder to remember. Using long descriptive names is a practice inherited from conventional compiler languages and are ill suited for interactive testing for starters. What's needed is a good thesaurus (in your head) and to stop constraining your thinking to conventional language syntax etc. How many times have I seen a word that may be named turn_on_red_led when in Forth I just say ON RED LED. Which is better? When I can just as easily say OFF BLUE LED or ON HOT WATER without having a special word for it then it makes a lot of sense.
As for stack juggling, if you are doing that then you may as well join a circus, at least you'll get paid for juggling. I detest the PICK and ROLL words, they are a poison and you never need to use them, I don't, and yet they are ANS, what does that say?. There is an advantage to the judicious use of local or global variables and I will use variables when it makes sense to. The rest of the time some simple stack manipulation is all that is needed.
This is probably more than I wanted to say but it's either food for though or fuel for the fire, take your pick.
Now to the problem at hand...
Just now I was looking at the butterfly loops of the fft and wondering how on earth I can tackle that monster. It has a pile of local variables. It occurred to me that those local variables are not really part of the actual problem that this code solves. They are used as scratch pad space and generally to make the calculation easier to read. So, I can get rid of most/all of them by merging the expressions into bigger expressions. For example this code: Can be transformed to this: Using short names as suggested makes it fit on the page at least!
So we have removed 9 local variables at the cost making it impossible to read. Never mind:)
Given that my arrays are global anyway I can evaluate the right side of those expressions in Forth using only the Forth stack and perhaps only a and b as globals (or can I hide them on the return stack?)
I still have those pesky array indices fi, bf, fs, w to worry about they are basically the parameters to this block.
Of course we can see a bunch of sub-expressions there that can be factored out into their own words.
So Peter, or any Forther's, is this a reasonable way to proceed? Any suggestions?
Yes, I understand that. No, it it most likely that any code you get from any source will need at least some change on any other platform or environment, same as with any other language. And you can insist that a language such as C is always completely portable from one compiler to the next, but I do not share that opinion as I have already cited my example that isn't.
I will also again advise you that in my experience, per the examples I have given in this thread, that taking forth code from another source and porting it to at least propforth is about equivalent to reading my own from six months prior and using on today's task. That is, getting the two square root functions to work on propforth was about as difficult as looking at the ANSI support extension and getting it to run on a new version of propforth (Last time it was edited I change the numeric literals to h_ and d_ prefixes, instead of having to keep track of the current cog's BASE).
This is NOT a requirement. This is a recommendation as a behavior of the most successful people, after decade of experience. Think about it. If you write one million lines of code before you test, you will probably have more than a million errors in it. AND every error will impact the rest of the code, so it will be impossible to to determine any change fixes something or breaks something else. Writing one line will probably have a few errors, but the effects of each error is easy to identify and fix.
You don't have to do it this way, but I used to program that way (write the whole thing before testing), and in my opinion "small bits" is much easier and faster. Which is why folks use forth. One CANNOT do this so easily in other environment, hence the appeal of forth.
It is your opinion and do anything anyway you want, but this NOT breaking stuff up into small easily solvable pieces is usually defined as a "bad habit". Yes it shows you are smarter than me, but makes significantly more work in the long run, particularly for a beginner, with heater as an example. .
From the original, I don't know if the second is corret:
You want arrays, bx, by, bz,wy, wx.
You want support functins to store to each array, example using by:
[code]
by! (value offset - by! )
by@ (offest by@ - value)
[code]
You want functions k1, k2, k3, tx, ty.
But that is all I can guess, I I never figured out the FFT stuff.
The C code that Heater has for the FFT is almost a one-to-one translation of the algorithm as written in mathematical notation. If we use global variables we should be able to translate this to Forth on a line-by-line basis. However, it won't be very efficient if we do it this way. Other languages have optimizing compilers that will generate efficient assembly, and there is no need to re-organize the code.