Shop OBEX P1 Docs P2 Docs Learn Events
The Forth Dimension. - Page 2 — Parallax Forums

The Forth Dimension.

245

Comments

  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 09:36
    Heater. wrote: »
    No news on local variables yet then.
    I also would appreciate the lowest common denominator way to set use arrays and look up tables.

    All vairables on the cog are local. All variable in hub are global.

    To "hide" a variable from further use, redefine it; subsequent uses of that identifier will only reach the most recetn definition.

    REALLY local stuff is the element on the stack.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-12-07 09:38
    Ok guys. Define what you mean by portability.

    Do you write assembler on one hardware, (x86) and expect it to be directly portable to another hardware (68k)? No

    Are you going to write code on the prop, and expect it to be directly portable on an ATEMGA? In ANY language? No.

    Are you going to write a BASIC program for the prop, and expect it to be directly portable to another BASIC on the prop? No. There will all ways be at least something different.

    IF you write in FORTH, is any one going to require you to run you program on all three current incarnation of forth with no changes. Possibly, but its still kind of silly.

    Please look at the ANSII terminal control support for propforth.

    http://propforth.googlecode.com/files/ANSI%20Escape%20Sequences20120704-1652.f

    This should pretty much run on ANY forth, as I developed it on the PC and ported it to propforth. Some stuff is different, but its the same for the most part. The things that make it different on the prop or the PC are so small that you don't even notice them. The effort is similar to when you notice spell "the" as "teh". You fix it and move on.

    So you have to change 1B to h1B. Or change h1B to $1B. How much is that going to kill you? Sheesh!

    Ok this numeric litteral thing is tough on your established habits, sorry about that. It is my fault.
    Of course you're correct that microcontroller programs won't necessarily be easy to port from one MCU to another no matter what language is used. I think we're talking here about generic algorithms though. An FFT is the same no matter what MCU it runs on and it ought to be possible to write it once and use it in multiple projects on multiple platforms. Same goes for things like CRC computation, sort algorithms, hash tables, etc. These things don't depend on the underlying hardware and it would be nice to be able to express them in a "portable" subset of whatever language is being used.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 09:43
    Heater. wrote: »
    Which means your nicely laid out pseudo code of an algorithm which is clear and concise gets chopped in to mess unintelligible snippets. .

    You are getting it backwards.

    If you have to mess with the stack so much, its a strong indication you may be going about it wrong.

    Typically each word consumes 0 to 2 values from the stack, and deposits 0-1 values on the stack. you debug each individual word until you know it works. Then you never have to worry about that parts again. So it doesn't matter how many words were already on the stack, or how many are going to be on the stack, this word only worries about these two it eats and that one it leaves. Its really simple. KISS is why it works well.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 10:09
    David Betz wrote: »
    An FFT is the same no matter what MCU it runs on and it ought to be possible to write it once and use it in multiple projects on multiple platforms. Same goes for things like CRC computation, sort algorithms, hash tables, etc. These things don't depend on the underlying hardware and it would be nice to be able to express them in a "portable" subset of whatever language is being used.

    As demonstarted with the ansii support linked, this is the case.
  • rod1963rod1963 Posts: 752
    edited 2012-12-07 10:50
    Braino

    You can write HLL code that is portable across platforms without modification or with minor adjustments as long as you don't do don't include direct hardware access or at least just leave that portion in a separate module. This includes MCU's. Data structures and algorithms should be portable across them as they are written in generic C and don't need to access to the hardware to accomplish their tasks.

    This is why Linux, BSD Unix and other OS's were able to be ported across a wide variety of hardware platforms so rapidly along with a large suite of applications.

    Forth should be the same way when it comes to higher level constructs like algorithms and data structures write once and be able to run on multiple platforms. Provided of course they would adhere to some standards.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-07 11:07
    Doug, I have ported C code that I found on the internet to PropGCC, and it ran the first time without changes. That's what I call portability. Requiring any changes to the source code impacts portability.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 11:18
    rod1963 wrote: »
    You can write HLL code that is portable across platforms without modification or with minor adjustments as long as you don't do don't include direct hardware access

    Precisely. FORTH, like assembler is pretty much EXACTLY direct hardware access, at the kernel level. (That is the point of the kernel).

    The "separate module" is what we cal "extensions", and can be added to support ANSI comatiblity, portablity, or anything else we desire.

    Folks get confused because the kernel APPEARS to be a full operating system, but it is not, and is not intended to be (at least not from the kernel perspective).
    This includes MCU's. Data structures and algorithms should be portable across them ....Forth should be the same way when it comes to higher level constructs like algorithms and data structures write once and be able to run on multiple platforms. Provided of course they would adhere to some standards.

    yes! exactly! This is where extensions come in. And this is how it "ought" to be done, and why propforth does what it does this way. But, there is no rule that forces folks to do this, and so so folks don't. But if everybody did things my way, the world would be a boring place.

    If folks want to build all the extensions stuff directly into the kernel, they are free to do so, this can even be done in propforth. But we are careful to keep the kernel free of extensions, and specifically identify when we build in developer extensions to the development kernel. The developer extensions can be removed without damage to the kernel, and will not damage a correctly written application ( one that does not call the removed developer functions).
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-07 11:26
    Doug, you make it sound so easy to port code to any Forth kernel. All you have to do is write a few words, right? I challenge you to port the chess program at http://www.ultratechnology.com/chess.html to PropForth. It took me about a day to port it to pfth because it uses some non-standard words that I had to implement first. Let's see how long it takes you to port it to PropForth. The version of code I used is the one in chess.zip at the end of the webpage. This is a text-only version.
  • D.PD.P Posts: 790
    edited 2012-12-07 11:43
    Dave Hein wrote: »
    Doug, I have ported C code that I found on the internet to PropGCC, and it ran the first time without changes. That's what I call portability. Requiring any changes to the source code impacts portability.

    Portability does not imply usefulness.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-07 13:39
    D.P wrote: »
    Portability does not imply usefulness.
    Yes, that is true. In the case of the chess program, I can run it under pfth, but it is so slow that it's not that useful. If I set the level to 2 it takes about a minute for the Prop to determine its next move. However, it's pretty easy to beat at that level. If I set the level to 3 it takes 15 to 25 minutes for it to compute it's next move, and it is harder to beat. I think I can optimize a few things to speed this up by at least a factor of 2. It spends a lot of time in CMOVE copying the chessboard for every move it evaluates.

    So even though portability does not imply usefulness, if you can't port a piece of code then you can't run it either.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 13:52
    Heater. wrote: »
    What is the "heater test"?

    The "heater" test is heat says "OK, this forth is usable, and I was able to use it to implement a proper FFT".

    :)
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 14:01
    Dave Hein wrote: »
    However, I usually encounter a few gForth-specific words that I have to convert to ANS Forth. You will need to implement several words to port this from gForth to PropForth or Tachyon.

    This is the typical case, since these come down to hardware differences and minor implementation differences due to design decisions. To say this renders the code non-portable would technically true, but could be seen as an overstatement, since it is generally a simple task.

    (I'm still catching up on this mornings posts, pardon me if we already resolved this).
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-07 14:13
    Dave Hein wrote: »
    Doug, you make it sound so easy to port code to any Forth kernel. All you have to do is write a few words, right? I challenge you to port the chess program at http://www.ultratechnology.com/chess.html to PropForth. It took me about a day to port it to pfth because it uses some non-standard words that I had to implement first. Let's see how long it takes you to port it to PropForth. The version of code I used is the one in chess.zip at the end of the webpage. This is a text-only version.

    In addition, one also has to understand the application to be implemented. I never wrote a chess game, and this would significant time to just to figure out what a chess program wants to do. Maybe somebody smart can do this but not me.

    That is not to claim I am not full of BS, this is entirely possible. In my exeriemce, forth is forth; if you can do something on one forth, you can probably do it on another. BUT if the task involves special hardware functions that are not common to all forths, then you might have some work to do. The prop has a LOT of architectural differences, etc; expect differences from a PC forth.
  • Heater.Heater. Posts: 21,230
    edited 2012-12-07 22:49
    Braino,
    Ok guys. Define what you mean by portability.
    Portability, as in SimpleIDE can be compiled to run on Windows, Linux, Mac and elsewhere. As in fft_bench can be compiled to run on any target that has a C compiler. You know all this.
    All vairables on the cog are local. All variable in hub are global.
    Useful. But not in gforth on my PC for example.
    To "hide" a variable from further use, redefine it; subsequent uses of that identifier will only reach the most recent definition.
    That would be perfect if I could subsequently undefine the most recent definition and resume using the previous one. Or are you say I can actually do that?
    You are getting it backwards. If you have to mess with the stack so much, its a strong indication you may be going about it wrong.
    What stack? My solution to the bit reversal problem does not require a stack, many algorithms don't, and there is none specified in the C implementation. There probably would not be one if I did it in assembler. Only in Forth do I have to worry about that pesky stack with dup and swap. So perhaps you are right, Forth is "doing it wrong".
    The "heater" test is heat says "OK, this forth is usable, and I was able to use it to implement a proper FFT".
    Ah, OK, I'll get back to working on it today.
  • Heater.Heater. Posts: 21,230
    edited 2012-12-08 04:54
    Forth guys,

    I need help. Seriously, I can feel my sanity slipping away...

    After six hours of tenacious stack twiddling I managed to produce a working integer square root word shown below.

    A few things I need help with:

    1. The common or garden integer square root has a while loop. I think I picked up somewhere that not all Prop Forth have a "while", is that true?
    Looking at the other Forth loop constructs I came to the conclusion I need an endless "begin...again" loop with an "exit". Is there a better loop construct available on all Prop Forths?

    2. Of course "exit" does not exit the loop it exits the word. So I end up creating a new word just to hold the loop. This is such a small function I'd like to see it in one word.

    3. sqrti has three local variables including its parameter. Given that not all Prop Forths have locals and if they did they would probably be incompatible with gforth, I could either a) use global variables, yuck. Or b) just mash things around on the stack. I chose the later. Was this wise or is the a better way?

    4) With keeping the locals on stack comes the severe migraine headaches. Below is a straightened out listing with the stack comments I required to keep everything straight. This is how it was written originally. How do you guys do this without reaching for the bottle every ten minutes?

    5) Why am I doing this? It's easier to write that function in PASM. What does Forth bring to the table?

    Any other comments/suggestions/criticism welcome.

    After a serious lie down I'll start on the decimation function.
    : sqrtiloop
        begin
            dup 0=  if exit then
            swap over or
            swap rot rot 2dup swap <=
            if
                swap over -
                swap rot swap over +
            else
                rot swap over -
            then
            1 rshift
            swap 2 rshift
        again
    ;
    
    
    \ Integer square root ( i -- s )
    : sqrti
       0 1 30 lshift sqrtiloop drop swap drop                         
    ;
    
    
    ." Square root of 1000000 = "
    1000000 sqrti . cr
    bye
    
    : sqrtiloop
        begin
            dup 0=                            
                                         ( i s t flag )
            if
                exit
            then
                                         ( i s t ) 
    
    
        \ s |= t
            swap                         ( i t s ) 
            over                         ( i t s t )
            or                           ( i t s )                                 
     
            \ if (s <= i)                             
            swap                         ( i s t )
            rot                          ( s t i )
            rot                          ( t i s ) 
            2dup                         ( t i s i s )  
            swap                         ( t i s s i )  
            <=                           ( t i s f ) 
            if
                \ i = i - s
                swap                     ( t s i )   
                over                     ( t s i s )   
                -
                                         ( t s i ) 
                \ s = s + t
                swap                     ( t i s )
                rot                      ( i s t ) 
                swap                     ( i t s ) 
                over                     ( i t s t ) 
                +                        ( i t s )            
            else
                \ s = s - t
                rot                      ( i s t )    
                swap                     ( i t s )    
                over                     ( i t s t )    
                -                        ( i t s )    
            then
            
            \ s = s >> 1
            1 rshift                     ( i t s )           
            \ t = t >> 2   
            swap                         ( i s t )    
            2 rshift                     ( i s t )    
        again
    ;
    
    
    \ Integer square root   ( i -- s )
    : sqrti
       0                                 ( i s )
       1 30 lshift                       ( i s t )
       sqrtiloop                         ( i s t )
       drop                              ( i s )
       swap                              ( s i )
       drop                              ( s )
    ;
    
    
    ." Square root of 100000 = " 1000000 sqrti . cr
    bye
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2012-12-08 04:59
    Firstly (and I will get back if I can about the other points) the WHILE construct has been on every Forth I have ever used and I can't understand why it has not been implemented in PF, it's not a big ask. Anyone who writes Forth applications beyond just a few snippets here and there are going to need a WHILE just to exit those endless loops graciously. So please just assume every Forth has it and if it hasn't then it should.

    I hate mashing the stack and would rather deal with a global variable. There is no need to be a Forth traditionalist as that would imply that Forth was created perfect but even Chuck doesn't use Forth like this. ColorForth is not ANS. Do what you have to do, as for me I use this interim form of local variables that I use or find another way.

    4) Any excuse to reach for the bottle will do, hic!
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 06:15
    Heater. wrote: »
    Portability, as in SimpleIDE can be compiled to run on (anything)... fft_bench ... any target that has a C compiler. You know all this.

    You are approaching this from the wrong direction. This will slow you down and cause frustration, as you notice.

    A friend from Moscow was visiting, I showed him dry-wall screws. He said "I know these, they are Russian Nails". He proceed to pound them into the wall with a hammer. I asked how does he get them to the correct depth, he said "Careful pounding". I ask how does he get them out with the head all smashed up, he said "I don't". You can use a hammer on drywall screws if you wish. There is a different method, using them like screws, which is how they were designed.

    Same applies to forth.

    We can can write blink LED and ASNI support, and it will pretty much work on any forth. (probably cosmetic changes, same as I notice on C).

    If you write something more interesting, there will likely be more interesting work to be done. As I said before, the code I encountered written for the Image Craft C compiler will NOT run on CGG or Catalina. I estimate that ANY C written for the prop will not be very portable, simply because of the differences in LIBRARIES. Demanding otherwise is likely "Russian Nails".

    DON'T think of forth kernel as a C compiler. Forth can only be as generic as the hardware its running on, because the KERNEL is at the hardware level.

    DO think of forth kernel as Assembler: Assembler for Intel is not expected to have much similarity to assembler for Motorola. Think VERY VERY EASY assembler.

    IF you want another layer (Hardware Abstraction Layer) this IS NOT part of the kernel. The kernel is just to get us to FORTH, and is not intended to suplly the HAL (except for pfth, which is unusual on a micro controller).

    Once we get to the forth kernel, a HAL may be implemented, but as Dave pointed out, Its a bunch of work on YOUR part, because the rest of us don't need it and therefore have not bothered with it. So don't fight the seasons, you tend to be on your own at that point. Not recommended.

    Once you complete your final, working application on one forth (the time should be very short compared to the same implementation in another exotic language) it is fairly trivial to port your code to any other forth on any other processor. While some changes are required to tailor to the new target architecture, you already have 90% competed code, and its a simple matter of debugging. If your case, you don't know how to "code simple" yet, so you won't get the benefit of "just debugging" till you get the swing of coding in forth. This will probably happen AFTER you complete you application (getting it working) and go over once and say "what idiot wrote this?" and refactor it properly with a more experienced eye. Its the normal learning process.

    Hang in there, it only took Loopy a couple weeks.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-12-08 06:36
    DON'T think of forth kernel as a C compiler. Forth can only be as generic as the hardware its running on, because the KERNEL is at the hardware level.

    DO think of forth kernel as Assembler: Assembler for Intel is not expected to have much similarity to assembler for Motorola. Think VERY VERY EASY assembler.

    Hmmm... If that's the way I should think of Forth, I think I'd rather just use PASM and get its big speed advantage. I'm not sure that's really the best way of thinking about Forth though.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 06:36
    Heater. wrote: »
    Forth guys,

    I need help. Seriously, I can feel my sanity slipping away...

    After six hours of tenacious stack twiddling I managed to produce a working integer square root word shown below.


    Maybe you should start at the beginning, with something simple? This is the recommended method.

    Also, until you reach guru level, each word (definition) should:

    1. consume 0-2 items from the stack, no more
    2. deposit 0-1 items on the stack, no more
    3. do no more that seven things, no more; three things MAX for a beginner is recommended.
    4. If you can't look at it and determine what its doing, its wrong. Break each definition down until it is simple.
    5. Debug each simple definition until you are absolute certain it does what its supposed to do, and use it for that purpose with absolute confidence.
    6. If you find you violate any of the guidelines, you are likely to have issues. Remember, these are guidelines and can be ignored, but not by a beginner.
    7. In you case, you are taking on task that likely requires one to violate the guidelines. This is a sign that you are approaching it wrong, or starting out too complex.
    8: AFTER you get the code working, you combine several of the simple functions together to reduce code size, improve efficiency, etc. But remember this is much easier with working code. To start you want LOTS of little simple functions that are easy to figure, rather than one huge huge difficult function that will make you crazy.

    Your code has lots of swap, rot, over sequences that undo each other. Refactor your code so they are gone or at a minimum. dup <evaluation> is the thing you need, can you get rid of redundant stack operations?.

    you have dup 0= if exit then
    factor this out, if possible.

    Did you look at nick's Fast Hartley? Actually, nick is the person that should critique you code, he has done what you are doing and in a better position to help. Ignore any comments I make if they contradict his.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 07:05
    David Betz wrote: »
    Hmmm... If that's the way I should think of Forth, I think I'd rather just use PASM and get its big speed advantage. I'm not sure that's really the best way of thinking about Forth though.

    Re: assembler

    If you can program directly in assembler, then yes of course you should. For most of us, programming in assembler take at least ten times longer than programming in any other language. So most people would NOT choose assembler, except on the one or two processor that have 10-15 years experience.

    If you program in forth, you can get nearly the same advantage on ANY processor. This is why experts feel forth has a benefit. Personally, after having to learn IBM360 assembler, I swore I would never learn another useless assembly again. With forth, I don't have to. I only have to look up the one or two instructions I need, use them, and get back to work.

    Anyway, this is just a hint to get heater started. All his C habits are in the way, we have to get him to suspend those for a moment, until he catches differences about forth.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 07:20
    Firstly ..WHILE construct ...please just assume every Forth has it and if it hasn't then it should.

    Please understand that Peter disagrees with some experts on this point.

    We can add the WHILE construct, go ahead. It does NOT need to be in the kernel, we already have looping constructs and dictionary space is scarce. On a PC, folks will throw it in since PC's are all about wasting resources (eg: Windows). It might be wandering off on a tangent, it might be how you learn best. We start with and empty tool box, and build our own custom tools. The experts say keep it simple, I try to keep it simple. Except when I start talking.

    FACTORING the code is the name of the game. In C, the compiler does all the work for you. In forth, we are conscious of the factoring according to our speed and size needs (ie ignore it if you don't care, pay attention if it matters). Factor the task until its broken down into a list of easily solvable simle functions. Solve them. Test them thoroughly, all the way down to the simplest, and all the way back up again to the full application (do not skip either part of this step). Refactor the working code to make it faster.

    Of primary importance is the quantity of time you spend coding. This is the skill we build. Until you log sufficient "flight time" you will not be an ace fighter pilot. Code it, refactor it, test it, recode it. Beat the snot out of a simple program until you get your wings, them move on to the next task.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-08 07:35
    prof_braino, most of what you have said presents good arguments AGAINST programming in Forth. Also, it doesn't take years to become an expert in PASM coding. The Prop's assembly language is elegant in its simplicity, and it took me about one week to become proficient in PASM programming. I think you exagerate the benefits of Forth, and never consider the drawbacks of Forth. The biggest limitation of Forth is that the programmer has to rewrite his algorithm to match Forth's limitations rather than coding his algorithm as it is described in a natural language.

    The main problem isn't the reverse Polish notation, but it is dealing with the darn stack. Most other high level languages don't have this limitation. Their compilers do a good job of generating efficient code that uses registers and keeps track of where things are on the stack.

    Heater, as a first cut at doing the FFT I would suggest that you put stuff in variables. Once you get it working in Forth using variables you can then optimize it by keeping more stuff on the stack, and breaking pieces up into smaller words.
  • Heater.Heater. Posts: 21,230
    edited 2012-12-08 09:34
    Braino,

    "You are approaching this from the wrong direction"
    Very possibly, seeing as I don't know where I am going. An Irish man was once asked for some directions to a place across town, he looked around and scratched his head. After a long pause for thought he said "If I was you...I would not start from here":)

    "Russian nails"
    I like it. Just now I feel like I have been trying to drive in nails with a screwdriver, can' find the slot.

    I think we are in agreement that any code in any language that relies on weird local features like those of hardware or operating systems will need work to port to another environment. However, code that only needs memory to work in, like an integer square root function, should not need any change unless the language is actually different. And that's our gripe here.

    "DON'T think of forth kernel as a C compiler". I don't think about the Forth kernel at all. I think about Forth the language, it syntax and semantics. They exist independently of any implementation.

    "DO think of forth kernel as Assembler"
    . It certainly feels that way.

    "VERY VERY EASY assembler."
    So far it's a very hard assembler. For the two bits of code I have made so far writing it in many assembler languages is easier.

    What you are saying is that Forth is a crude as assembler, a non-portable HLL and a million time slower. I start to wonder what is the point. I'm sure many Forth supporters would disagree with you.

    "Maybe you should start at the beginning, with something simple" What? How simple can a thing be?. sqrti and bitreverse are 13 and 7 lines of simple code in C and Go and probably about the same in PASM. Of course I had to do a lot of one line experiments along the way to get a feel of the language.

    As for your list of "dos and don'ts" I agree in general. BUT this idea of breaking such a simple thing down to many more simple things just obfuscates the algorithm even more. Worse still it's the wrong way to do it. What you end up with is a mess of individually meaningles and useless little snippets. Each with a name that you don't really need. What you actually needed was names for the variables involved that have now become anonymous.

    "Your code has lots of swap, rot, over sequences"
    I'm sure that is true. It's a first pass in a new language. Perhaps you could show how it's done.

    "Did you look at nick's Fast Hartley?"
    I have in the past. However that is not relevant here. As I said before the fact that I choose an FFT as a first Forth program is of no consequence. Just think of it as some bunch of calculations and logic that needs to be done and the C/Go/Spin versions are the specification in pseudo code. It's just an exercise.

    "If you can program directly in assembler, then yes of course you should."
    Not really. Not unless I need the speed and/or space. If I'm going to invest a lot of time in some code might as well try to make something that can be reused and moved from platform to platform.

    "This is just a hint to get heater started. All his C habits are in the way, we have to get him to suspend those for a moment, until he catches differences about forth." I think you are making a big assumption there. This spring I had to learn JavaScript and produce a few thousand lines of it. JS looks very C like so it's easy to approach it like C. However, despite it's looks, JS is a very different language and C habits can be a trap. JS also looks like C++ but tying to use it like will get you into a mess.

    Just recently I have been writing in Go. Again it looks like C and therefore it's easy to approach it like C. However it is also a very different language.

    Luckily, Forth looks so alien it's unlikely that the C half of my brain wakes up at all:)
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 13:11
    OK, I sent heater a PM offering to step through the process, but then I got carried away and just did it myself. Sorry, we might have to try another program as a walkthrough. But here's this one.


    OK. I don't know how to do square root, so I look it up in wiki pedia and get a headache.
    I give up on trying to understand the algorithm and decide to just copy, and I look at any example of square root in forth.
    I google: sqaure root forth and the first one is: --- http://jasonwoof.org/sqrt.fs

    I translate the weird begin while +loop from gforth
    into the weird begin 0= until in propforth
    : sqrt-closer \ ( square guess -- square guess adjustment) 
                  2dup / over - 2 / ;
    
    
    : sqrt \ ( square -- root )   start with our number on tthe stack 
    
           1             \ first guess is 1 
           0             \  first adjustment is zero
           begin 
                 +           \ add the result to guess
                 sqrt-closer \ target guess - target guess adjustment
          \       st?         \ comment this out when done
                 dup         \ test the adjustment
                 0=          \ if result not zerro do it again
           until             \ if result not zerro do it again
           drop           \ get rid of guess 
    
           nip  \        get rid of old target
           \ .              \ print the result  (instead of nip)
           \ drop           \ get rid of  the old target (instead of nip)            
       ;
    
    : sqrt-test 2000000000 1 do i dup . sqrt . cr loop ;
    : test 20 1 do i dup .  cr loop ;
    

    Its still running but so far it looks right.

    I don't know if this is a valid demonstration of portability, but it was just translating his construct into my construct, and then it just ran.

    This is a little harder than a complete standard library in C, and a little easier than a complete re-write in assembler.

    Start time was a bout 13:22 and end time was about 15:06, and included trying to understand the algorithm to code it from scratch, which required more patience than I could spare at the time.

    I didn't figure out what sqrt-closer was doing, I just checked that the right number of items were on the stack before and after.
    The main effort was making sure the flag was handled properly by the loop.
    The real forth work took about half an hour, but as I said before I don't do coding, I'm just documentation and test.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-08 13:35
    prof_braino, that looks like a good solution. I think Heater's sqrt is based on the algorithm Chip uses, which takes 16 loops. I did a modified version of Heater's code using UNTIL, and storing one of the variables on the return stack. That uses a few less words and less stack shuffling, and might be a little easier to follow than Heater's.
    hex
    : sqrt ( x -- y )
      >r                   \ Save x
      0 40000000           \ y = 0, z = 40000000
      begin
        dup rot or         \ y |= z
        dup r@ >           \ y > x
        if
          over -           \ y -= z
        else
          dup r> swap - >r \ x -= y
          over +           \ y += z
        then
        1 rshift           \ y >>= 1
        swap 2 rshift      \ z >>= 2
        dup 0 =
      until
      r> 2drop             \ return y
    ;
    decimal
    
  • Heater.Heater. Posts: 21,230
    edited 2012-12-08 13:58
    Braino, Dave,

    Thank for the efforts. They look much nicer already.

    However on my gforth Braino's sqrti hangs for inputs of 3, 5, 8, 12, 15, ....An input of zero produce a divide by zero error.

    Dave's runs fine on gforth. It's about 10% faster tan mine.

    I found that algorithm on the net somewhere. The idea was to avoid any divides and multiplies.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-12-08 15:03
    Sorry , maybe it wasn't gforth, looking back jasonwoof says retroforth but didn't say what he was running on.

    Anyway, I ran it on propforth. If I do the jasonwoof thing with the +loop to skip the redundant rounded solutions, it never hits zero and goes off forever, so I'd have to figure out what to do if this is needed.

    Propforth doesn't have r@ (I guess it saves two more longs in the cog?), so I did
    r> dup >r
    

    I avoid using the return stack as a temp anyway , somebody always screws is up, and I'm not in a big hurry. But as long as nobody is messing with the code, it works fine.

    I didn't do any speed tests but is gives the same results as the other code, and they both completes before the return key rises back from key press.
    hex   \ chips
    : sqrt \ ( x -- y )
      >r                   \ Save x
      0 40000000           \ y = 0, z = 40000000
      begin
        dup rot or         \ y |= z
        dup r> dup >r >           \ y > x
        if
          over -           \ y -= z
        else
          dup r> swap - >r \ x -= y
          over +           \ y += z
        then
        1 rshift           \ y >>= 1
        swap 2 rshift      \ z >>= 2
        dup 0 =
      until
      r> 2drop             \ return y
    ;
    decimal
    
    : sqrt-test h0FFF_FFFF   2 do i dup . sqrt . cr  i  +loop ;
    

    So, even though forth is "not portable" in the strictest sense between different versions of forth, the porting process is pretty straightforward and quick. Its about as difficult as debugging for typos. The offending word is flagged (as a typo), we look at what that word wanted to do, and we replace with code that does that function.

    EDIT - Followup - I left this running, and it was still going when I went to bed, and it finished sometime in the night. At least 4 hours, but less than 14 hours. For some reason I thought it would run a lot longer. It looks like it reset at the end, so there is still a bug someplace. Probably the +loop isn't hiting the end case correctly.
    Prop0 Cog6 ok      6773796 2602
    CON:Prop0 Cog0 RESET - last status: 0 ok
    Prop0 Cog6 ok
    CON:Prop0 Cog1 RESET - last status: 0 ok
    
    CON:Prop0 Cog2 RESET - last status: 0 ok
    
    CON:Prop0 Cog3 RESET - last status: 0 ok
    
    CON:Prop0 Cog4 RESET - last status: 0 ok
    
    CON:Prop0 Cog5 RESET - last status: 0 ok
    Prop0 Cog5 ok
    
  • Heater.Heater. Posts: 21,230
    edited 2012-12-08 15:29
    Ok that works on my gforth as well.

    Quick meaningless timing test shows Dave's to be quickest mine to be slowest and Braino's modified version in the middle. Only about 10% differences.
  • Heater.Heater. Posts: 21,230
    edited 2012-12-08 17:45
    What to do about those arrays?

    I am reading that to define and access data in arrays I need stuff like:
    CREATE X 10 CELLS ALLOT   \ Allocate a 10 cell array
    137 X 5 CELLS + !        \ Write 137 to element 5 
    X 5 CELLS + @ .          \ Read element 5
    
    Needless to say such ugliness does not inspire me. Is there a way to neaten this up? What about some accessors? May be like this:
    \ The FFT real and complex buffers
    create bx 1024 cells allot
    create by 1024 cells allot
    
    
    : bx.get   ( element -- value )
        bx swap cells + @
    ;
    
    
    : bx.set   ( element value -- )
        swap dup >r  swap
        bx r> cells + !
        drop
    ;
    
    
    \ Write 42 to the 5th element
    5 42 bx.set
    
    
    \ Read the 5th element
    5 bx.get . cr
    
    This looks nice and could even include array bounds checking but is not very general. I'd need a set of accessors for every array.

    Any suggestions?
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-12-08 18:02
    For a more general array access don't hard-code the array address in the accessor words. You could do something like this:
    : get ( element addr -- value ) swap cells + @ ;
    : set ( value element addr -- ) swap cells + ! ;
    
    \ Write 42 to element 5 of bx
    42 5 bx set
    
    \ Read element 5 of bx
    5 bx get
    
    If you can use DOES> you can streamline this a bit by adding "does> cells +" after the create, so it would look like this.
    : array create cells allot does> cells + ;
    1024 array bx
    1024 array by
    
    \ Write 42 to element 5 of bx
    42 5 bx !
    
    \ Read element 5 of bx
    5 bx @
    
    If the cells word is implemented with "4 *" you could speed it up by using "2 lshift" instead. On most Forths you can do a "see cells" to see how it is defined.
Sign In or Register to comment.