Yes, the the table driven approach like MJBs is the approach I have since my first days of Forth, nice and clean. If you keep the columns lined up in the table it will look even neater which is also why I don't use the standard (and ugly) C, and W, for tables and prefer vertical lines which don't clutter the code and make it look more like a table as in MJB's example. But I also have code tables that can be indexed directly using LOOKUP which I will give an example of later but here is the comment from the source file:
{ Table vectoring -
index a table of vectors and jump to that vector
A table limit is supplied as well as a default vector
Usage:
<limit> VECTORS <vector if over>
<vector0> <vector1> ...... <vectorx>)
Sample:
4 LOOKUP BELL \ an index of 4 or more will default to BELL
INDEX0 INDEX1 INDEX2 INDEX3 \ 0 to 3 will execute corresponding vectors
}
However for your exercise the method I presented will make it work.
Seems like the newer version of SDCARD.fth will not work with the Prop BOE because of the pullup on CS ?
Shouldn't be any problem with SDCARD or even with having a pullup on "CD/DAT3/CS" (CD=card detect!) as SDCARD will just think that there is a card in there anyway. Did you try another card that you know has worked?
You can of course add a card detect method much like the C3 mod if you want although that is optional I guess and not this problem you are seeing. On looking at the code it looks like the C3 method has a bug in it too(ucard=C3CARD !!!), I must check to see what's happened there.
Anyway use the Forth Dave to locate the problem, !SD should return with a CRC or zero if it failed, that's a good start.
Hi there, thanks to all who gave me input but I'm still struggling with my statemachine code. Peter gave an advice which seemed to me "most appropriate to my mindset" so I tried:
WORD nextstate
WORD currstate
WORD laststate
ALIAS : State
: AST ( -- ) \ Apply State Transition
currstate W@ laststate W!
nextstate W@ currstate W!
\ not yet currstate W@ CALL
;
: PNS ( nextstate -- ) \ Prepare Next State
nextstate W!
;
0 == 'sOne
0 == 'sTwo
State sOne
CR ." One"
'sTwo PNS
;
State sTwo
CR ." Two"
'sTwo PNS
;
\ Peter's: ' sOne TO 'sOne
\ Peter's: ' sTwo TO 'sTwo
\ Peter's: ' sThree TO 'sThree
' sOne == 'sOne
' sTwo == 'sTwo
: RUN ( -- ) \ run method
'sOne currstate W! \ initially preset currstate of statemachine
'sOne nextstate W! \ initially preset currstate of statemachine
'sOne laststate W! \ initially preset currstate of statemachine
\ 10 TIMES AST NEXT \ normally infinite loop
;
HEX
RUN
Then I did interactively:
...
AST ok
nextstate W@ ok
. 4264 ok
nextstate W@ CALL
One ok
nextstate W@ . 0 ok
The last line drives me crazy. Why isn't there the execution token of `sTwo´ in nextstate? It seems that the 0 from the earlier `0 == sTwo´ is still in effect if `PNS´ is called.
@Peter: BTW I didn't get managed with the `TO´ word you gave me for homework :-) I made some attempts with `==´ and `W!´ but had no success.
Hi there, thanks to all who gave me input but I'm still struggling with my statemachine code. Peter gave an advice which seemed to me "most appropriate to my mindset" so I tried:
WORD nextstate
WORD currstate
WORD laststate
ALIAS : State
: AST ( -- ) \ Apply State Transition
currstate W@ laststate W!
nextstate W@ currstate W!
\ not yet currstate W@ CALL
;
: PNS ( nextstate -- ) \ Prepare Next State
nextstate W!
;
0 == 'sOne
0 == 'sTwo
State sOne
CR ." One"
'sTwo PNS
;
State sTwo
CR ." Two"
'sTwo PNS
;
\ Peter's: ' sOne TO 'sOne
\ Peter's: ' sTwo TO 'sTwo
\ Peter's: ' sThree TO 'sThree
' sOne == 'sOne
' sTwo == 'sTwo
: RUN ( -- ) \ run method
'sOne currstate W! \ initially preset currstate of statemachine
'sOne nextstate W! \ initially preset currstate of statemachine
'sOne laststate W! \ initially preset currstate of statemachine
\ 10 TIMES AST NEXT \ normally infinite loop
;
HEX
RUN
Then I did interactively:
...
AST ok
nextstate W@ ok
. 4264 ok
nextstate W@ CALL
One ok
nextstate W@ . 0 ok
The last line drives me crazy. Why isn't there the execution token of `sTwo´ in nextstate? It seems that the 0 from the earlier `0 == sTwo´ is still in effect if `PNS´ is called.
@Peter: BTW I didn't get managed with the `TO´ word you gave me for homework :-) I made some attempts with `==´ and `W!´ but had no success.
Hi there, thanks to all who gave me input but I'm still struggling with my statemachine code. Peter gave an advice which seemed to me "most appropriate to my mindset" so I tried:
WORD nextstate
WORD currstate
WORD laststate
ALIAS : State
: AST ( -- ) \ Apply State Transition
currstate W@ laststate W!
nextstate W@ currstate W!
\ not yet currstate W@ CALL
;
: PNS ( nextstate -- ) \ Prepare Next State
nextstate W!
;
0 == 'sOne
0 == 'sTwo
State sOne
CR ." One"
'sTwo PNS
;
State sTwo
CR ." Two"
'sTwo PNS
;
\ Peter's: ' sOne TO 'sOne
\ Peter's: ' sTwo TO 'sTwo
\ Peter's: ' sThree TO 'sThree
' sOne == 'sOne
' sTwo == 'sTwo
: RUN ( -- ) \ run method
'sOne currstate W! \ initially preset currstate of statemachine
'sOne nextstate W! \ initially preset currstate of statemachine
'sOne laststate W! \ initially preset currstate of statemachine
\ 10 TIMES AST NEXT \ normally infinite loop
;
HEX
RUN
Then I did interactively:
...
AST ok
nextstate W@ ok
. 4264 ok
nextstate W@ CALL
One ok
nextstate W@ . 0 ok
The last line drives me crazy. Why isn't there the execution token of `sTwo´ in nextstate? It seems that the 0 from the earlier `0 == sTwo´ is still in effect if `PNS´ is called.
@Peter: BTW I didn't get managed with the `TO´ word you gave me for homework :-) I made some attempts with `==´ and `W!´ but had no success.
executing a
val == myCONST
again will NOT redefine the constant, but instead create a new one which shadows the old one,
so your code from before the new definition continues to use the old one. ...
Hi there, thanks to all who gave me input but I'm still struggling with my statemachine code. Peter gave an advice which seemed to me "most appropriate to my mindset" so I tried:
<snip>
0 == 'sOne
0 == 'sTwo
First off.
The purpose of == is the same as CONSTANT is in Forth, to define a constant although I prefer a symbol instead which does not detract from seeing the value and the name stand out more than a distracting CONSTANT. So:
4 CONSTANT four
or
4 == four
Where I prefer the latter as I focus on 4 and four rather than the overpowering CONSTANT
WHY USE THESE CONSTANTS?
Now the whole reason I introduced constants for code addresses into your code was simply to handle any cases of what otherwise would be forward referencing which is not handled normally in the single-pass back referencing to what it "already knows" manner that Forth works. So I could keep those definitions of yours happy as they could refer to a defined (by then) word (a constant) at compile time but that this constant could be tinkered with later on in the compile once the actual definitions had "become known" by Forth that we could tell it "set this constant with the address of this code". So all the constant does is hold a vector which btw could have been done with a variable but we NEVER want to change this vector once we set it as it represents its namesake and was only created to handle this case of forward referencing.
UNDER THE HOOD
Okay, now when we define a constant it is meant to be read-only as we are used to however remember that a typical Forth constant is not a sealed box and we can always open up the same box and rewire it so that everything that has been connected to it now gets a different value from that box, the "constant". But you say "that's what variables are for, you can't change a constant!" which is true for a compiled language's binary blob but in Forth that constant has its own name, code, and data space that can be tinkered with and so we do as we have the advantages of a cleaner looking constant which can be referenced without a C@/W@/@ that a variable would require each and every time.
To tinker with a constant though requires a bit more knowledge about how that constant is stored which is unlike a variable which simply returns with the address of the data space. In Tachyon Forth a constant is an independent definition which when called is simply:
[OPCODE FOR "READ CONSTANT"] [LONG VALUE OF CONSTANT]
The opcode itself is a bytecode that is positioned just before a LONG boundary so that the long value can be read by the Prop with a single RDLONG. Here is the kernel code:
' Long aligned constant - created with CONSTANT and already aligned
CONL
rdlong X,IP ' get constant
jmp #PUSHX_EXIT
This constant does not reveal where it is in memory though, it only returns with the value of the constant. To change it would require that we know the address where the value is held. We can do this in Tachyon using the tick symbol which finds the code address of <name>" like this:
1234 == mycon ok
mycon . 1234 ok
' mycon .WORD 41BF ok
' mycon 10 DUMP
0000_41BF: 7F D2 04 00 00 72 41 BF 73 0A C0 BD 0C 0C 0C 00 .....rA.s....... ok
' mycon 1+ @ . 1234 ok
5678 ' mycon 1+ ! ok
' mycon 10 DUMP
0000_41BF: 7F 2E 16 00 00 72 41 BF 73 0A C0 BD 0C 0C 0C 00 .....rA.s....... ok
mycon . 5678 ok
The word TO that is also used is just a shortcut on the ' <constant> 1+ !
Now this next bit is a fundamental mistake.
<snip>
' sOne == 'sOne
' sTwo == 'sTwo
You have created two new constants with the same name as the old one which still exists and to which your previous definitions are still "wired" to. So the new constants by the same name are unconnected to anything and do not change the outcome.
Now the "pièce de résistance"
Then I did interactively:
...
AST ok
nextstate W@ ok
. 4264 ok
nextstate W@ CALL
One ok
nextstate W@ . 0 ok
The last line drives me crazy. Why isn't there the execution token of `sTwo´ in nextstate? It seems that the 0 from the earlier `0 == sTwo´ is still in effect if `PNS´ is called.
@Peter: BTW I didn't get managed with the `TO´ word you gave me for homework :-) I made some attempts with `==´ and `W!´ but had no success.
sTwo did exactly what you told it to do:
0 == 'sTwo
State sOne
CR ." One"
'sTwo PNS
;
So 'sTwo that is referenced is 0 which is what you read. The newer 'sTwo that you later defined is not referenced or connected to anything. Perhaps the problem here is that you may be used to an interpretive Lisp-like language in a PC environment where it doesn't need to run in real-time but can afford to interpret source code and thus allow processing redefinitions dynamically. However everything in Tachyon is compiled as you type and statically linked and any new definitions by the same name are independent of the previous but have the effect that any new references to this name always refer to the latest. There is however a mechanism in Tachyon where old definitions can be "redefined" but normally only for debug purposes and the explanation of which is best left out here.
Hooray, the statemachine is rudimentarily running. This is how it goes:
WORD nextstate
WORD currstate
WORD laststate
ALIAS : State
: AST ( -- ) \ Apply State Transition
currstate W@ laststate W!
nextstate W@ currstate W!
currstate W@ CALL
;
: PNS ( nextstate -- ) \ Prepare Next State
nextstate W!
;
State sUndef
CR ." Undef"
;
' sUndef == 'sUndef
' sUndef == 'sOne
' sUndef == 'sTwo
' sUndef == 'sThree
: RUN ( -- ) \ run method
'sOne currstate W! \ initially preset currstate of statemachine
'sOne nextstate W! \ initially preset currstate of statemachine
'sOne laststate W! \ initially preset currstate of statemachine
\ 10 TIMES AST NEXT \ normally infinite loop
;
State sOne
CR ." One"
'sTwo PNS
;
State sTwo
CR ." Two"
'sThree PNS
;
State sThree
CR ." Three"
'sOne PNS
;
' sOne TO 'sOne
' sTwo TO 'sTwo
' sThree TO 'sThree
HEX
RUN
BTW: Does anybody have an idea how to hide the redundant
' sOne TO 'sOne
In something like this:
: EndState
' sOne TO 'sOne
;
Or something more templatized than this
: State
' sOne TO 'sOne
;
Is there a chance to do this? Therefore one would need access to the name of a `word´ inside that `word´ I made some attempts but my knowledge is not advanced enough to handle GETWORD to access the name, concat/prefix this with a single quote ' maybe an EVAL. For me it's impossible but for you geniusses out there ... :-)?
Thanks to all participants. Now I can wiggle forthward on my new way :-)
Hooray, the statemachine is rudimentarily running.
BTW: Does anybody have an idea how to hide the redundant
' sOne TO 'sOne
In something like this:
: EndState
' sOne TO 'sOne
;
Not quite sure what you are asking here. You are assigning the CFA of a action word to a constant as you know, what is redundancy? Perhaps your state machine is not refactored for Tachyon and the Prop optimally. Looking at lots of Peter's FORTH I find his BIG loop being called by the lazy keypoll ( approx 8KHz). He is using various timers as periodic event "dispatchers/handlers/checkers" and one or more cogs doing various tasks with global words/vars/tables and his form of IPC. The benefit of his approach is the console is always available while the robot is working so you can poke and peek at its guts! Also I see his "state machines" as more problem domain driven (using meaningful action words) than the formalized transition type.
But this is how we learn. Peter is just so productive with his tools it's scary.
Good job on your Tachyon code.
Try this.
Call your main state action word "loop" from keypoll
keypoll calls your word (which can now be up to 8 words I believe) about 8k per second whenever the keyboard is idle. This leaves the console free for you to use!! This output is way too fast for humans/console so try this
long lcnt --- long to hold counter
0 lcnt ! --- I like positive initialization
pub myactionword
lcnt @ 1+ DUP lcnt ! --- increment counter and leave a copy on TOS
#8000 > --- compare it to 8k
IF
0 lcnt ! --- reset and display
." Output to console " CR
THEN
;
' myactionword keypoll W! --- start it up with keypoll
--- 0 keypoll W! --- type this to kill it or reboot without a backup else it will autorun which is very nice too
As an exercise can you make this so you could turn it on and off using simple action words such as log+ and log- ?
Could you make it display faster or slower using simple action words and using MIN and MAX so there is always a floor and ceiling for speed? How about log++ and log-- for action words. Neat thing is you only need to type the speed command once then just use CTRL X to repeat the command to get the display where you want it.
I have this setup on my bots so when they/I need to spew to the terminal I turn it on and such. That way I can turn it off and poke at the guts during debug. Incredibly handy.
Next up is creating your first Tachyon Module just like Peter's stuff. Encapsulation and conditional compilation just like the ++r's
Hooray, the statemachine is rudimentarily running. This is how it goes:
WORD nextstate
WORD currstate
WORD laststate
ALIAS : State
: AST ( -- ) \ Apply State Transition
currstate W@ laststate W!
nextstate W@ currstate W!
currstate W@ CALL
;
: PNS ( nextstate -- ) \ Prepare Next State
nextstate W!
;
State sUndef
CR ." Undef"
;
' sUndef == 'sUndef
' sUndef == 'sOne
' sUndef == 'sTwo
' sUndef == 'sThree
: RUN ( -- ) \ run method
'sOne currstate W! \ initially preset currstate of statemachine
'sOne nextstate W! \ initially preset currstate of statemachine
'sOne laststate W! \ initially preset currstate of statemachine
\ 10 TIMES AST NEXT \ normally infinite loop
;
State sOne
CR ." One"
'sTwo PNS
;
State sTwo
CR ." Two"
'sThree PNS
;
State sThree
CR ." Three"
'sOne PNS
;
' sOne TO 'sOne
' sTwo TO 'sTwo
' sThree TO 'sThree
HEX
RUN
BTW: Does anybody have an idea how to hide the redundant
' sOne TO 'sOne
In something like this:
: EndState
' sOne TO 'sOne
;
Or something more templatized than this
: State
' sOne TO 'sOne
;
Is there a chance to do this? Therefore one would need access to the name of a `word´ inside that `word´ I made some attempts but my knowledge is not advanced enough to handle GETWORD to access the name, concat/prefix this with a single quote ' maybe an EVAL. For me it's impossible but for you geniusses out there ... :-)?
Thanks to all participants. Now I can wiggle forthward on my new way :-)
even if you have a good reason to not use a table based approach -
at least save yourself a lot of complication and code
and instead of using the word adresses just use an integer to denote the state number -
just a very natural way to do it.
then create a vector of words - and a little magic (untested - too wet here ;-)
BYTE state
17 WORDS myStateFuns
pub NextState state C@ * myStateFuns + W@ CALL state C! ;
pub STATEFUN IMMEDIATE @NAMES NFA>CFA >PFA state C@ 2* myStateFuns + W! state C++ ;
pub sZero STATEFUN
CR ." Zero"
1 NextState
;
we could make pub XX STATEFUN in a DEFSTATE but thats a little more magic
As D.P and MJB pointed out, there are different ways to approach this but of course you are learning the language at the same time and it is natural to use it in the way we are familiar with. The main thing is to get it functional first and then experiment from there, hence the "Hooray!". I think the boys might be a bit excited that they can help out a newcomer
One of my first exercises in learning Forth was a very simple Point of Sale terminal (my first application). I would define different kinds of fruit as counters so that I could type something similar to this:
6 BANANAS
4 APPLES
8 ORANGES
SUB
10 DOLLARS
TOTAL
and at the end of the day I could report sales
REPORT
Of course the final POS software was absolutely nothing like this, all I was trying to do was learn and also think about how I could leverage the "language" in simplifying the code and the approach, which it did immensely. If I had simply recoded in Forth from another language it would have been an ugly mess. Look in my Tachyon docs folder and find the book "Thinking Forth".
BTW, this exercise was on an Apple II computer so the words were quite appropriate
Alright guys, I'm calling a party foul here. Can we do a little more laundry on the Glossary before you head back to Zen land in hardware, Forth and Lua please.
I was thinking during all these posts that I must do something about the use and definition of NFA CFA PFA as they are in traditional Forth but mean something not quite the same in Tachyon. The trouble is that I do have the two bytes in the name header which usually are the byte code(s) to compile or they could be in some cases a 16-bit pointer to the bytecodes to execute. What do I call this field? Then there is the actual code itself, be it in cog as PASM or in hub as bytecode, what do I call this?
NFA is probably ok but just remembering that the name space is separate from code space. Now putting aside for the moment the two bytes in the header that are the compilation codes we have the actual code which is either PASM in the cog (opcode) or bytecode in the hub. When we do a tick of an opcode it returns with its address in the cog in the range of $00..$FF or $100..$1FF in the case of XOP access to the second 256 longs in the cog. Okay, that's fine but some words are comprised of 2 opcodes as we allow 2 bytes in the header to represent the code. A example is NIP which rather than consuming precious cog memory for code is simply defined as SWAP DROP and an attribute in the header tells Tachyon to compile these two bytes. Now if we were to find the tick (code address) of NIP we would get something but not what we were expecting, it is more of an unresolved situation. I would like to think of CFA more as a "Compile Field Address" taking into account the attribute as well while the PASM or byte code is either machine code or VM code so it is still a "Code Field Address" or CFA. Whereas PFA referred to the parameters that immediately followed the code field header in traditional Forths, so those parameters could be a constant, a structure, or more typically CFAs of Forth words to execute.
Looking at NIP we have.
' NIP .WORD 2F26 ok
NFA' NIP $10 DUMP
0000_6A0D: 03 4E 49 50 86 1F 11 03 41 44 4F 86 1E D1 02 32 .NIP....ADO....2 ok
' SWAP .WORD 001F ok
' DROP .WORD 0011 ok
where we can see the 2 compilation codes just after the name and attribute $86
Very first two bytecodes that are compiled are SWAP ($1F) and DROP ($11) which are the PASM addresses or opcodes which represent NIP. The C1 following that is a CALL to a vector via the index 64 that then looks up the address that points to the actual bytecode to execute for PRINT" after which we can see the null terminated string followed by another call C1 67 which is . or PRINT and then 0C which is the EXIT opcode.
Great, but this is not straightforward threaded Forth. Perhaps CFA can mean the code address to execute and we can still use PFA to point to the header code bytes which are compiled. If PFA is too confusing because of traditional PFA meaning then I would favor CCA for Compilation Code Address so using as an example:
NFA' PRINT" 10 DUMP
0000_6584: 06 50 52 49 4E 54 22 A2 C1 71 05 50 52 49 4E 54 .PRINT"..q.PRINT ok
At present we can say:
NFA' PRINT" 10 DUMP
0000_6584: 06 50 52 49 4E 54 22 A2 C1 71 05 50 52 49 4E 54 .PRINT"..q.PRINT ok
NFA' PRINT" NFA>CFA .WORD 658C ok
which actually finds the CCA which then needs to be converted to the CFA. At present the CFA and PFA are jumbled, my bad. But then CCA should really point to the preceding attribute to ascertain the correct interpretation of the following two compilation code bytes. Now if you go to TF2 it is a lot simpler again but the P1 is awkward to implement a traditional Forth in and the result would be less than satisfactory so therefore the awkwardness is in the implementation so that the result is a Forth that is more than satisfactory.
Hooray, the statemachine is rudimentarily running. This is how it goes:....
even if you have a good reason to not use a table based approach -
at least save yourself a lot of complication and code
and instead of using the word adresses just use an integer to denote the state number -
just a very natural way to do it.
then create a vector of words - and a little magic (untested - too wet here ;-)
BYTE state
17 WORDS myStateFuns
pub NextState state C@ * myStateFuns + W@ CALL state C! ;
pub STATEFUN IMMEDIATE @NAMES NFA>CFA >PFA state C@ 2* myStateFuns + W! state C++ ;
pub sZero STATEFUN
CR ." Zero"
1 NextState
;
we could make pub XX STATEFUN in a DEFSTATE but thats a little more magic
I was thinking during all these posts that I must do something about the use and definition of NFA CFA PFA as they are in traditional Forth but mean something not quite the same in Tachyon.
Hi Peter,
I am just getting used to NFA/CFA/PFA and fine with it and I don't really care what they do in other forth's.
So no need to change or rename anthing and shake up my little understanding again.
btw. your LOOKUP will not work with 16-bit adressing using 3 bytes - right? I don't need it, just was thinking when reading the source ..
Maybe a formal description on how to do what other Forth's do with CREATE ... DOES> would be nice to have.
And helpful in implementing such more advanced structures like a 'defState' ...
Hi Peter,
I am just getting used to NFA/CFA/PFA and fine with it and I don't really care what they do in other forth's.
So no need to change or rename anthing and shake up my little understanding again.
btw. your LOOKUP will not work with 16-bit adressing using 3 bytes - right? I don't need it, just was thinking when reading the source ..
Maybe a formal description on how to do what other Forth's do with CREATE ... DOES> would be nice to have.
And helpful in implementing such more advanced structures like a 'defState' ...
The PFA and CFA use is still a little jumbled but "tick of" returns the code address or CFA which technically in Tachyon is the same as PFA So perhaps PFA does get dropped in favor of CCA meaning the address of the compile codes in the header.
With LOOKUP it always assumes that the 16-bit address is compiled, not the compilation codes, so it doesn't matter whether the code is normally compiled as a XCALL (index) or as a WCALL (16-bit).
Now as mentioned Tachyon is rather unconventional in its implementation so it does not actually have a CFA and PFA which normally lends itself to CREATE....DOES>. Yes, it would be nice to have these words and I will have to think further as to how I will implement them, both in P1 and in P2.
Hi Peter,
I am just getting used to NFA/CFA/PFA and fine with it and I don't really care what they do in other forth's.
So no need to change or rename anthing and shake up my little understanding again.
btw. your LOOKUP will not work with 16-bit adressing using 3 bytes - right? I don't need it, just was thinking when reading the source ..
Maybe a formal description on how to do what other Forth's do with CREATE ... DOES> would be nice to have.
And helpful in implementing such more advanced structures like a 'defState' ...
The PFA and CFA use is still a little jumbled but "tick of" returns the code address or CFA which technically in Tachyon is the same as PFA So perhaps PFA does get dropped in favor of CCA meaning the address of the compile codes in the header.
With LOOKUP it always assumes that the 16-bit address is compiled, not the compilation codes, so it doesn't matter whether the code is normally compiled as a XCALL (index) or as a WCALL (16-bit).
Now as mentioned Tachyon is rather unconventional in its implementation so it does not actually have a CFA and PFA which normally lends itself to CREATE....DOES>. Yes, it would be nice to have these words and I will have to think further as to how I will implement them, both in P1 and in P2.
I agree with MJB here, don't really care about other FORTHS because similar to assemblers they seem to all be uniquely tied to the hardware somewhat. All I would like is a better understanding which you are doing on these pages, thanks. And also so anyone venturing into this fun zone I can try to help as best as possible. The more I work with the system on real projects the easier it becomes to shed my procedural ways and use the language of the problem domain to get to a simple, maintainable solution.
Going further down this rabbit hole it seems to attain the speed and size from P1 you made a beautiful FrankenForth.
You used a Direct Threaded Code model added some Token Threaded Code and finished it off with some Indirect Threaded Code or maybe that is Subroutine Threaded if I'm following along here somewhat.
Now to look at P2 to see what you did when I have a moment. Tachyon is making me work, I like that.
Looking at that link about Forths I'd say it didn't anticipate the Propeller! The closest they seem to get to Tachyon bytecode is Token Threaded Code but they seem to think that it would be limited to 256 words and that it would be much slower. Boy, are they are wrong! The interesting thing about TF bytecode is that there is no "extra indirection" as we use the bytecode as a direct address into the first 256 longs of cog code. As for only 256 words I can't imagine why they would even say that but it only takes one opcode (unique bytecode) to say "use another byte to index into the second page", and a few more opcodes to say "call via table x using the next byte as an index". Then there is the opcode that says "just read the next two bytes as a 16-bit call address" or "call the address that is on the data stack". Wow, what's the limit!? (btw, since a bytecode is an address, we can only use some of the 256 combinations possible)
Another thing about P1 TF is that I didn't want a horrible looking source code that was buried in all the operators necessary to make each word compile or hide all that by going through special "compile to Spin compatible" tools. I just wanted a single page of source that was clearly readable as Forth source could be in the Spin tool environment. This is where Brad C's BST helped a lot too as I could view the code listing and rearrange source to make sure everything was in the optimum place. Practically all compilers these days fail to produce absolute code listings which I think is rather sad but also leaves you in the dark as to what has actually been compiled. Anyway because having to work in with the Spin tool and keep the source code as uncluttered as possible I developed the XCALL method using half of a long in a vector table so that a higher level word could be called easily as in "XCALL,xEMIT" for instance. (the other otherwise unusable half gets used later by the Forth compiler itself for more vectors)
Having a separate name space has meant I could save on a link field which is also clutter at "assembler" level but the separate name space has also permitted an easy way to reclaim unnecessary headers (pri) and also allows them to be reorganized as in EEWORDS which indexes them into upper EEPROM to save memory.
There are heaps of different under-the-hood features too in TF that is very different from standard Forths, even just the use of loop and branch stacks for instance. The list goes on but it's all about creating a powerful synergy between the Prop and the software to make what might not be possible, possible.
Looking at that link about Forths I'd say it didn't anticipate the Propeller! The closest they seem to get to Tachyon bytecode is Token Threaded Code but they seem to think that it would be limited to 256 words and that it would be much slower. Boy, are they are wrong! The interesting thing about TF bytecode is that there is no "extra indirection" as we use the bytecode as a direct address into the first 256 longs of cog code. As for only 256 words I can't imagine why they would even say that but it only takes one opcode (unique bytecode) to say "use another byte to index into the second page", and a few more opcodes to say "call via table x using the next byte as an index". Then there is the opcode that says "just read the next two bytes as a 16-bit call address" or "call the address that is on the data stack". Wow, what's the limit!? (btw, since a bytecode is an address, we can only use some of the 256 combinations possible)
Another thing about P1 TF is that I didn't want a horrible looking source code that was buried in all the operators necessary to make each word compile or hide all that by going through special "compile to Spin compatible" tools. I just wanted a single page of source that was clearly readable as Forth source could be in the Spin tool environment. This is where Brad C's BST helped a lot too as I could view the code listing and rearrange source to make sure everything was in the optimum place. Practically all compilers these days fail to produce absolute code listings which I think is rather sad but also leaves you in the dark as to what has actually been compiled. Anyway because having to work in with the Spin tool and keep the source code as uncluttered as possible I developed the XCALL method using half of a long in a vector table so that a higher level word could be called easily as in "XCALL,xEMIT" for instance. (the other otherwise unusable half gets used later by the Forth compiler itself for more vectors)
Having a separate name space has meant I could save on a link field which is also clutter at "assembler" level but the separate name space has also permitted an easy way to reclaim unnecessary headers (pri) and also allows them to be reorganized as in EEWORDS which indexes them into upper EEPROM to save memory.
There are heaps of different under-the-hood features too in TF that is very different from standard Forths, even just the use of loop and branch stacks for instance. The list goes on but it's all about creating a powerful synergy between the Prop and the software to make what might not be possible, possible.
Forths are not alone in that. The kind of shift in microcontroller design and thinking required to create and make use of the Propeller is pretty hard to anticipate.
Hello tachyonauts, for my statemachinery I'd like to have higher resoluted system time. I know there is the CNT@ register, but it is a bit too high resoluted. I would be glad with 0.1 seconds happy with 10 ms. This is what I'm doing:
long time \ this is `system time´
long dtick \ delta between calls of UPDTIME
long currtick \ CNT@
long lasttick \ last 16 bit CNT@ since UPDTIME
1 == timebase \ divider for system clock (96_000_000 timebase / )
: FAKE ;
: STACKTIMES $7FFF_FFFF 3 - DUP 1+ DUP 1+ DUP 1+ DUP 1+ DUP 1+ DUP 1+ ;
\ ' CNT@ == 'timefake
' FAKE == 'timefake
: UPDTIME ( -- ) \ update systime has to be called at a rate < 2 32 ^ CLKFREQ /
currtick @ lasttick !
'timefake CALL ABS currtick ! \ sample current time
time @ 0=
IF \ only in first cycle or at reset
." reset system time" CR
currtick @ NEGATE time !
\ time 1+
THEN
currtick @ lasttick @ - dtick ! \ calc dtick
dtick @ 0 =>
IF
0 \ 0 to tos
ELSE
." overrun" CR
$FFFF_FFFF timebase / \ overrun to tos
THEN
dtick @ + time @ + time !
;
0 time ! \ hold current time
STACKTIMES
UPDTIME CR lasttick @ . SPACE currtick @ . SPACE dtick @ . SPACE time @ . SPACE CR .S
\ ' UPDTIME keypoll W!
I'm working with simulated CNT@ via 'timefake which fetches new timeclock from stack. This is quite clever (I'm proud to be able to do this :-) but my attempts to write genious one liners currently tend to become a one-million liner.
When I started I thought there is this CNT@ overflow I will get managed with it, but now I must accept that this small timer problem isn't so small as I thought of. Maybe the solution is small but I currently don't yet see it. My code is a mess. I want to solve this myself but doing so there are already many hours gone. Therefore any suggestions are welcome. Ah ... is there already a solution in EXTEND I have overseen?
Hello tachyonauts, for my statemachinery I'd like to have higher resoluted system time. I know there is the CNT@ register, but it is a bit too high resoluted. I would be glad with 0.1 seconds happy with 10 ms. This is what I'm doing:
long time \ this is `system time´
long dtick \ delta between calls of UPDTIME
long currtick \ CNT@
long lasttick \ last 16 bit CNT@ since UPDTIME
1 == timebase \ divider for system clock (96_000_000 timebase / )
: FAKE ;
: STACKTIMES $7FFF_FFFF 3 - DUP 1+ DUP 1+ DUP 1+ DUP 1+ DUP 1+ DUP 1+ ;
\ ' CNT@ == 'timefake
' FAKE == 'timefake
: UPDTIME ( -- ) \ update systime has to be called at a rate < 2 32 ^ CLKFREQ /
currtick @ lasttick !
'timefake CALL ABS currtick ! \ sample current time
time @ 0=
IF \ only in first cycle or at reset
." reset system time" CR
currtick @ NEGATE time !
\ time 1+
THEN
currtick @ lasttick @ - dtick ! \ calc dtick
dtick @ 0 =>
IF
0 \ 0 to tos
ELSE
." overrun" CR
$FFFF_FFFF timebase / \ overrun to tos
THEN
dtick @ + time @ + time !
;
0 time ! \ hold current time
STACKTIMES
UPDTIME CR lasttick @ . SPACE currtick @ . SPACE dtick @ . SPACE time @ . SPACE CR .S
\ ' UPDTIME keypoll W!
I'm working with simulated CNT@ via 'timefake which fetches new timeclock from stack. This is quite clever (I'm proud to be able to do this :-) but my attempts to write genious one liners currently tend to become a one-million liner.
When I started I thought there is this CNT@ overflow I will get managed with it, but now I must accept that this small timer problem isn't so small as I thought of. Maybe the solution is small but I currently don't yet see it. My code is a mess. I want to solve this myself but doing so there are already many hours gone. Therefore any suggestions are welcome. Ah ... is there already a solution in EXTEND I have overseen?
there is a Timer Task running with 1ms resolution. you can just have your state machine called every 10 ms if the runtime of all tasks is not longer than 1 ms and interferes with the timer itself.
Otherwise set up an extra COG to run your state machine at given interval.
@proplem: The background timer task maintains a linked list of timers on a 1ms basis and you can setup a new timer to countdown to zero or count up instead and also optionally have an alarm condition that is executed when it counts down to zero. Otherwise you can read "runtime" which counts milliseconds from reset. Then again simply create an "Elapsed?" word that does a CNT@ then divides that by 80,000 to give you milliseconds.
BTW, this is how you would normally add a timer to your app which automatically links itself to the list of timers maintained by the timer task.
TIMER mytimer (create a timer structure)
100 mytimer TIMEOUT (will timeout in 100ms)
BTW in regards to keypoll it is best not to access this variable directly as it already is set to point to a general-purpose polling routine that handles many keypoll events. Just use +POLL to set a new keypoll event like this: ' <mypoll> +POLL
You can have up to 8 different keypoll events which are very handy for all those lazy background checks but especially useful since it runs from your main cog which is important if any of those keypolls access I/O that is "owned" by the main cog.
Thank you very much, `runtime´ is what I wanted to build myself. For learning purpose I'd like to look how it is implemented but I don't find it - nor in Tachyon V2.9.spin neither in EXTEND.
Thank you very much, `runtime´ is what I wanted to build myself. For learning purpose I'd like to look how it is implemented but I don't find it - nor in Tachyon V2.9.spin neither in EXTEND.
LINE 2974 in EXTEND.fth has this:
pub TIMERTASK ( DESC: Provide background timing functions including alarm actions on timeouts )
timerstk SP! --- Setup a datastack for this cog
1 timers W! --- reset timer chain
3 tid C+! --- timer boot id - if timer id is same as this then it has already been added since boot
runtime ~ --- clear variables
ttint DELTA --- set the WAITCNT DELTA for every millisecond
wdt 8 ERASE --- disable watchdog
0 wdt TIMEOUT --- but link it into the timer list
' REBOOT wdt ALARM --- set default watchdog behaviour
BEGIN
timerjob W@ ?DUP IF CALL timerjob W~ THEN --- allow other cogs to get this cog to handle I/O inits etc
runtime ++
CountDown --- maintain the linked chain of countdown timers
@rtc NOT IF time ++ THEN
$F000.0000 INPUTS --- make sure no alarm routine affects these lines (in this cog).
WAITCNT --- synch to next heartbeat
AGAIN
;
Which code editor are you using as they all should find words easily?
Which code editor are you using as they all should find words easily?
Now I see - at morning I was in a hurry so I was quick searching for `runtime' a lot of matches and `runtime\s-+\!' no matches :-(
So the error was in front of the editor :-)
PS: It is surprising how difficult implementing my own counter from CNT@ was for me. Diving into these profane simplicities, shows where the ground is from which one has taken off. Thank you for having done this lot of work.
Just going through EXTEND.fth to see what I can tidy up and I thought I'd compile a list of functions that are standard in it, quite surprising really. Some of these functions are from the kernel but accessed at the high level through extend:
* BLINKY in one word ( <pin> BLINK ) using counters (no CPU)
* SPI setup and run - 4MHz 8/16/24/32-bit operations
* 32 channel 8-bit 7.6kHz PWM (1 cog)
* I/O operations - HIGH, LOW, etc
* Serial terminal operations including ANSI control and number formatting - up to 3M baud
* Delays in microseconds, milliseconds, and seconds
* MCP3208 A/D including conversion to volts etc
* SERIN and SEROUT bit-bashed serial coms (PBASIC style) => 200k baud
* Display I/O configuration including detected pullup/down etc.
* I2C drivers - fast
* EEPROM including block load/save/fill/copy
* COG COUNTER support - generate Hz/kHz/MHz frequencies, output RC DAC mode, etc
* Linked millisecond countdown timers with alarm on timeout functions
* Soft watchdog and RTC
* I2C RTC driver including formatting
* PING distance to millimeter or time to microseconds
* DHT22 style humidity and temperature
* WS2812 RGB LED driver - fast buffer mode, just specify the buffer, count, and pin
* RCTIME
* Ping Pong RS485/single wire multidrop full-duplex networking
* VGA text mode including big digits
* Terse command mode - every character received can be set as an immediate action
* Multi-tasking support in other cogs
* Maths and string functions and many others etc
Most of these functions are as easy to use as connecting up so a ping module to say pins 4 and 5 and measure the distance by interactively typing in:
4 5 DISTANCE
where the distance in mm is returned on the stack which you could just print that to the VGA (load optional VGA.fth for formatting)
Then there is the SD FAT32 virtual memory drivers as well as the WIZnet drivers and the EASYNET FTP/HTTP/TELNET servers as well as many other devices that have been interfaced.
But but it's all FORTH and so non-millennial and not C.
It takes effort, then comes the simplicity. Wish you had peers on this list that wanted to "look/play" at the assembler of Tachyon for optimizations/tricks. Kuroneko, da Silva, Hippy and many others are MIA unfortunately.
I'm just hanging on to my stack because you produce stuff faster than I can understand at this point still, getting better though
But but it's all FORTH and so non-millennial and not C.
It takes effort, then comes the simplicity. Wish you had peers on this list that wanted to "look/play" at the assembler of Tachyon for optimizations/tricks. Kuroneko, da Silva, Hippy and many others are MIA unfortunately.
I'm just hanging on to my stack because you produce stuff faster than I can understand at this point still, getting better though
I'm so tempted to say goodbye to these forums sometimes as they are very limited and not many forumistas seem to be active with actual Propeller hardware projects, that is, not meaning tools or learning. I think I'd be more productive in an Arduino ARM forum!
I try to make stuff really simple to use though so if you don't find it that way then please give me some feedback on what you think could be tweaked (within reason of course).
Like I said it's getting easier all the time and more fun. And there is so much less syntax and code to read through Well if you do go ARM yourself then leave crumbs for me to follow, I really don't care about the cpu anymore but Tachyon is key for me now. I don't think I can leave the FORTH.
Like I said it's getting easier all the time and more fun. And there is so much less syntax and code to read through Well if you do go ARM yourself then leave crumbs for me to follow, I really don't care about the cpu anymore but Tachyon is key for me now. I don't think I can leave the FORTH.
That's the thing, I can port a Forth to an ARM CPU easily as I have written Forths for these chips before and the thing is that these Forths can be very portable at the higher level too, as portable as I want to make them. I have some tiny M0 chips that could easily host a Forth and still have plenty of memory plus using that ping-pong protocol I can easily network them together while still maintaining full console level access to each one from any other one. For about the same cost as a P1 I can have about 4 ARM CPUs connected together as required but with a lot more memory. That's why I'm very tempted at times to just switch although I would really love to have P2 chips in production gear already and that desire is messing up my commercial decisions.
I demonstrated this networked console access to a friend today, chatted to one device, typed in a one-liner to run a song and dance and then I chatted to all the devices globally with the same one-liner and away they went. The other day I did the same thing except over Telnet to the P2 and then over the bus to the networked devices. Gotta love that kind of flexibility.
Comments
However for your exercise the method I presented will make it work.
Shouldn't be any problem with SDCARD or even with having a pullup on "CD/DAT3/CS" (CD=card detect!) as SDCARD will just think that there is a card in there anyway. Did you try another card that you know has worked?
You can of course add a card detect method much like the C3 mod if you want although that is optional I guess and not this problem you are seeing. On looking at the code it looks like the C3 method has a bug in it too(ucard=C3CARD !!!), I must check to see what's happened there.
Anyway use the Forth Dave to locate the problem, !SD should return with a CRC or zero if it failed, that's a good start.
@Peter: BTW I didn't get managed with the `TO´ word you gave me for homework :-) I made some attempts with `==´ and `W!´ but had no success.
The TO word is described HERE as
TO ( val <name> -- ) HI Assign a new value to an existing constant (variable constant) - only works in interactive mode!
executing a again will NOT redefine the constant, but instead create a new one which shadows the old one,
so your code from before the new definition continues to use the old one. ...
The purpose of == is the same as CONSTANT is in Forth, to define a constant although I prefer a symbol instead which does not detract from seeing the value and the name stand out more than a distracting CONSTANT. So: Where I prefer the latter as I focus on 4 and four rather than the overpowering CONSTANT
WHY USE THESE CONSTANTS?
Now the whole reason I introduced constants for code addresses into your code was simply to handle any cases of what otherwise would be forward referencing which is not handled normally in the single-pass back referencing to what it "already knows" manner that Forth works. So I could keep those definitions of yours happy as they could refer to a defined (by then) word (a constant) at compile time but that this constant could be tinkered with later on in the compile once the actual definitions had "become known" by Forth that we could tell it "set this constant with the address of this code". So all the constant does is hold a vector which btw could have been done with a variable but we NEVER want to change this vector once we set it as it represents its namesake and was only created to handle this case of forward referencing.
UNDER THE HOOD
Okay, now when we define a constant it is meant to be read-only as we are used to however remember that a typical Forth constant is not a sealed box and we can always open up the same box and rewire it so that everything that has been connected to it now gets a different value from that box, the "constant". But you say "that's what variables are for, you can't change a constant!" which is true for a compiled language's binary blob but in Forth that constant has its own name, code, and data space that can be tinkered with and so we do as we have the advantages of a cleaner looking constant which can be referenced without a C@/W@/@ that a variable would require each and every time.
To tinker with a constant though requires a bit more knowledge about how that constant is stored which is unlike a variable which simply returns with the address of the data space. In Tachyon Forth a constant is an independent definition which when called is simply:
[OPCODE FOR "READ CONSTANT"] [LONG VALUE OF CONSTANT]
The opcode itself is a bytecode that is positioned just before a LONG boundary so that the long value can be read by the Prop with a single RDLONG. Here is the kernel code:
This constant does not reveal where it is in memory though, it only returns with the value of the constant. To change it would require that we know the address where the value is held. We can do this in Tachyon using the tick symbol which finds the code address of <name>" like this: The word TO that is also used is just a shortcut on the ' <constant> 1+ !
Now this next bit is a fundamental mistake. You have created two new constants with the same name as the old one which still exists and to which your previous definitions are still "wired" to. So the new constants by the same name are unconnected to anything and do not change the outcome.
Now the "pièce de résistance"
sTwo did exactly what you told it to do: So 'sTwo that is referenced is 0 which is what you read. The newer 'sTwo that you later defined is not referenced or connected to anything. Perhaps the problem here is that you may be used to an interpretive Lisp-like language in a PC environment where it doesn't need to run in real-time but can afford to interpret source code and thus allow processing redefinitions dynamically. However everything in Tachyon is compiled as you type and statically linked and any new definitions by the same name are independent of the previous but have the effect that any new references to this name always refer to the latest. There is however a mechanism in Tachyon where old definitions can be "redefined" but normally only for debug purposes and the explanation of which is best left out here.
BTW: Does anybody have an idea how to hide the redundant In something like this: Or something more templatized than this Is there a chance to do this? Therefore one would need access to the name of a `word´ inside that `word´ I made some attempts but my knowledge is not advanced enough to handle GETWORD to access the name, concat/prefix this with a single quote ' maybe an EVAL. For me it's impossible but for you geniusses out there ... :-)?
Thanks to all participants. Now I can wiggle forthward on my new way :-)
But this is how we learn. Peter is just so productive with his tools it's scary.
Good job on your Tachyon code.
Try this.
Call your main state action word "loop" from keypoll
keypoll calls your word (which can now be up to 8 words I believe) about 8k per second whenever the keyboard is idle. This leaves the console free for you to use!! This output is way too fast for humans/console so try this
As an exercise can you make this so you could turn it on and off using simple action words such as log+ and log- ?
Could you make it display faster or slower using simple action words and using MIN and MAX so there is always a floor and ceiling for speed? How about log++ and log-- for action words. Neat thing is you only need to type the speed command once then just use CTRL X to repeat the command to get the display where you want it.
I have this setup on my bots so when they/I need to spew to the terminal I turn it on and such. That way I can turn it off and poke at the guts during debug. Incredibly handy.
Next up is creating your first Tachyon Module just like Peter's stuff. Encapsulation and conditional compilation just like the ++r's
Go Proplem
even if you have a good reason to not use a table based approach -
at least save yourself a lot of complication and code
and instead of using the word adresses just use an integer to denote the state number -
just a very natural way to do it.
then create a vector of words - and a little magic (untested - too wet here ;-) we could make pub XX STATEFUN in a DEFSTATE but thats a little more magic
As D.P and MJB pointed out, there are different ways to approach this but of course you are learning the language at the same time and it is natural to use it in the way we are familiar with. The main thing is to get it functional first and then experiment from there, hence the "Hooray!". I think the boys might be a bit excited that they can help out a newcomer
One of my first exercises in learning Forth was a very simple Point of Sale terminal (my first application). I would define different kinds of fruit as counters so that I could type something similar to this:
6 BANANAS
4 APPLES
8 ORANGES
SUB
10 DOLLARS
TOTAL
and at the end of the day I could report sales
REPORT
Of course the final POS software was absolutely nothing like this, all I was trying to do was learn and also think about how I could leverage the "language" in simplifying the code and the approach, which it did immensely. If I had simply recoded in Forth from another language it would have been an ugly mess. Look in my Tachyon docs folder and find the book "Thinking Forth".
BTW, this exercise was on an Apple II computer so the words were quite appropriate
NFA is probably ok but just remembering that the name space is separate from code space. Now putting aside for the moment the two bytes in the header that are the compilation codes we have the actual code which is either PASM in the cog (opcode) or bytecode in the hub. When we do a tick of an opcode it returns with its address in the cog in the range of $00..$FF or $100..$1FF in the case of XOP access to the second 256 longs in the cog. Okay, that's fine but some words are comprised of 2 opcodes as we allow 2 bytes in the header to represent the code. A example is NIP which rather than consuming precious cog memory for code is simply defined as SWAP DROP and an attribute in the header tells Tachyon to compile these two bytes. Now if we were to find the tick (code address) of NIP we would get something but not what we were expecting, it is more of an unresolved situation. I would like to think of CFA more as a "Compile Field Address" taking into account the attribute as well while the PASM or byte code is either machine code or VM code so it is still a "Code Field Address" or CFA. Whereas PFA referred to the parameters that immediately followed the code field header in traditional Forths, so those parameters could be a constant, a structure, or more typically CFAs of Forth words to execute.
Looking at NIP we have. where we can see the 2 compilation codes just after the name and attribute $86
Compare this with a definition that uses NIP: Very first two bytecodes that are compiled are SWAP ($1F) and DROP ($11) which are the PASM addresses or opcodes which represent NIP. The C1 following that is a CALL to a vector via the index 64 that then looks up the address that points to the actual bytecode to execute for PRINT" after which we can see the null terminated string followed by another call C1 67 which is . or PRINT and then 0C which is the EXIT opcode.
Great, but this is not straightforward threaded Forth. Perhaps CFA can mean the code address to execute and we can still use PFA to point to the header code bytes which are compiled. If PFA is too confusing because of traditional PFA meaning then I would favor CCA for Compilation Code Address so using as an example: At present we can say: which actually finds the CCA which then needs to be converted to the CFA. At present the CFA and PFA are jumbled, my bad. But then CCA should really point to the preceding attribute to ascertain the correct interpretation of the following two compilation code bytes. Now if you go to TF2 it is a lot simpler again but the P1 is awkward to implement a traditional Forth in and the result would be less than satisfactory so therefore the awkwardness is in the implementation so that the result is a Forth that is more than satisfactory.
even if you have a good reason to not use a table based approach -
at least save yourself a lot of complication and code
and instead of using the word adresses just use an integer to denote the state number -
just a very natural way to do it.
then create a vector of words - and a little magic (untested - too wet here ;-) we could make pub XX STATEFUN in a DEFSTATE but thats a little more magic Hi Peter,
I am just getting used to NFA/CFA/PFA and fine with it and I don't really care what they do in other forth's.
So no need to change or rename anthing and shake up my little understanding again.
btw. your LOOKUP will not work with 16-bit adressing using 3 bytes - right? I don't need it, just was thinking when reading the source ..
Maybe a formal description on how to do what other Forth's do with CREATE ... DOES> would be nice to have.
And helpful in implementing such more advanced structures like a 'defState' ...
The PFA and CFA use is still a little jumbled but "tick of" returns the code address or CFA which technically in Tachyon is the same as PFA So perhaps PFA does get dropped in favor of CCA meaning the address of the compile codes in the header.
With LOOKUP it always assumes that the 16-bit address is compiled, not the compilation codes, so it doesn't matter whether the code is normally compiled as a XCALL (index) or as a WCALL (16-bit).
Now as mentioned Tachyon is rather unconventional in its implementation so it does not actually have a CFA and PFA which normally lends itself to CREATE....DOES>. Yes, it would be nice to have these words and I will have to think further as to how I will implement them, both in P1 and in P2.
I agree with MJB here, don't really care about other FORTHS because similar to assemblers they seem to all be uniquely tied to the hardware somewhat. All I would like is a better understanding which you are doing on these pages, thanks. And also so anyone venturing into this fun zone I can try to help as best as possible. The more I work with the system on real projects the easier it becomes to shed my procedural ways and use the language of the problem domain to get to a simple, maintainable solution.
You used a Direct Threaded Code model added some Token Threaded Code and finished it off with some Indirect Threaded Code or maybe that is Subroutine Threaded if I'm following along here somewhat.
Now to look at P2 to see what you did when I have a moment. Tachyon is making me work, I like that.
Another thing about P1 TF is that I didn't want a horrible looking source code that was buried in all the operators necessary to make each word compile or hide all that by going through special "compile to Spin compatible" tools. I just wanted a single page of source that was clearly readable as Forth source could be in the Spin tool environment. This is where Brad C's BST helped a lot too as I could view the code listing and rearrange source to make sure everything was in the optimum place. Practically all compilers these days fail to produce absolute code listings which I think is rather sad but also leaves you in the dark as to what has actually been compiled. Anyway because having to work in with the Spin tool and keep the source code as uncluttered as possible I developed the XCALL method using half of a long in a vector table so that a higher level word could be called easily as in "XCALL,xEMIT" for instance. (the other otherwise unusable half gets used later by the Forth compiler itself for more vectors)
Having a separate name space has meant I could save on a link field which is also clutter at "assembler" level but the separate name space has also permitted an easy way to reclaim unnecessary headers (pri) and also allows them to be reorganized as in EEWORDS which indexes them into upper EEPROM to save memory.
There are heaps of different under-the-hood features too in TF that is very different from standard Forths, even just the use of loop and branch stacks for instance. The list goes on but it's all about creating a powerful synergy between the Prop and the software to make what might not be possible, possible.
Forths are not alone in that. The kind of shift in microcontroller design and thinking required to create and make use of the Propeller is pretty hard to anticipate.
When I started I thought there is this CNT@ overflow I will get managed with it, but now I must accept that this small timer problem isn't so small as I thought of. Maybe the solution is small but I currently don't yet see it. My code is a mess. I want to solve this myself but doing so there are already many hours gone. Therefore any suggestions are welcome. Ah ... is there already a solution in EXTEND I have overseen?
there is a Timer Task running with 1ms resolution. you can just have your state machine called every 10 ms if the runtime of all tasks is not longer than 1 ms and interferes with the timer itself.
Otherwise set up an extra COG to run your state machine at given interval.
https://docs.google.com/document/d/1tMlS3Oa8Sqa4LD90inoA9-JfhzuG_8m-yphxWHdFlZM/pub
see chapter 10.3
BTW, this is how you would normally add a timer to your app which automatically links itself to the list of timers maintained by the timer task.
TIMER mytimer (create a timer structure)
100 mytimer TIMEOUT (will timeout in 100ms)
BTW in regards to keypoll it is best not to access this variable directly as it already is set to point to a general-purpose polling routine that handles many keypoll events. Just use +POLL to set a new keypoll event like this: ' <mypoll> +POLL
You can have up to 8 different keypoll events which are very handy for all those lazy background checks but especially useful since it runs from your main cog which is important if any of those keypolls access I/O that is "owned" by the main cog.
Which code editor are you using as they all should find words easily?
So the error was in front of the editor :-)
PS: It is surprising how difficult implementing my own counter from CNT@ was for me. Diving into these profane simplicities, shows where the ground is from which one has taken off. Thank you for having done this lot of work.
* BLINKY in one word ( <pin> BLINK ) using counters (no CPU)
* SPI setup and run - 4MHz 8/16/24/32-bit operations
* 32 channel 8-bit 7.6kHz PWM (1 cog)
* I/O operations - HIGH, LOW, etc
* Serial terminal operations including ANSI control and number formatting - up to 3M baud
* Delays in microseconds, milliseconds, and seconds
* MCP3208 A/D including conversion to volts etc
* SERIN and SEROUT bit-bashed serial coms (PBASIC style) => 200k baud
* Display I/O configuration including detected pullup/down etc.
* I2C drivers - fast
* EEPROM including block load/save/fill/copy
* COG COUNTER support - generate Hz/kHz/MHz frequencies, output RC DAC mode, etc
* Linked millisecond countdown timers with alarm on timeout functions
* Soft watchdog and RTC
* I2C RTC driver including formatting
* PING distance to millimeter or time to microseconds
* DHT22 style humidity and temperature
* WS2812 RGB LED driver - fast buffer mode, just specify the buffer, count, and pin
* RCTIME
* Ping Pong RS485/single wire multidrop full-duplex networking
* VGA text mode including big digits
* Terse command mode - every character received can be set as an immediate action
* Multi-tasking support in other cogs
* Maths and string functions and many others etc
Most of these functions are as easy to use as connecting up so a ping module to say pins 4 and 5 and measure the distance by interactively typing in:
4 5 DISTANCE
where the distance in mm is returned on the stack which you could just print that to the VGA (load optional VGA.fth for formatting)
Then there is the SD FAT32 virtual memory drivers as well as the WIZnet drivers and the EASYNET FTP/HTTP/TELNET servers as well as many other devices that have been interfaced.
It takes effort, then comes the simplicity. Wish you had peers on this list that wanted to "look/play" at the assembler of Tachyon for optimizations/tricks. Kuroneko, da Silva, Hippy and many others are MIA unfortunately.
I'm just hanging on to my stack because you produce stuff faster than I can understand at this point still, getting better though
I'm so tempted to say goodbye to these forums sometimes as they are very limited and not many forumistas seem to be active with actual Propeller hardware projects, that is, not meaning tools or learning. I think I'd be more productive in an Arduino ARM forum!
I try to make stuff really simple to use though so if you don't find it that way then please give me some feedback on what you think could be tweaked (within reason of course).
That's the thing, I can port a Forth to an ARM CPU easily as I have written Forths for these chips before and the thing is that these Forths can be very portable at the higher level too, as portable as I want to make them. I have some tiny M0 chips that could easily host a Forth and still have plenty of memory plus using that ping-pong protocol I can easily network them together while still maintaining full console level access to each one from any other one. For about the same cost as a P1 I can have about 4 ARM CPUs connected together as required but with a lot more memory. That's why I'm very tempted at times to just switch although I would really love to have P2 chips in production gear already and that desire is messing up my commercial decisions.
I demonstrated this networked console access to a friend today, chatted to one device, typed in a one-liner to run a song and dance and then I chatted to all the devices globally with the same one-liner and away they went. The other day I did the same thing except over Telnet to the P2 and then over the bus to the networked devices. Gotta love that kind of flexibility.