Great news!!! I've been running into the dreading "dictionary is full" error after loading all the networking goodies and wondering what I need/can chop out. Look forward to testing this new functionality when ready.
As the eternal optimist was heard saying as he plummeted from the top of the tall building "so far, so good". The SDWORDS conversion (71 bytes) just works and all the information seems to be there fine.
Typing in this definition:
pub LOOK ( <name> -- ) IMMEDIATE --- just a handy debug to dump the relevant word sector
GETWORD 1- C@++ 7 SHL SWAP C@ + 9 SHL FILE@ + $100 SD DUMP
;
then using it to have a look at a word sector:
So this is the sector that would be presented as the "dictionary" whenever I type in a 7 character word that starts with a capital C. The nulls at the end of the words would then be seen as an end of dictionary if there wasn't a match and it would either try to convert it to a number or fail to find the word which is normal.
Your threats to move the dictionary out of ram seem to be materializing nicely. Many people have good ideas, but very few can pull them from the "ether" and place them into reality.
Now I want to use a GPS receiver module and get the NMEA sentences over the serial port.
Is there already a NMEA receive module for Tachyon somewhere?
I tried to get the serial data with some:
31 SERIN serb !
Where "serb" is of BYTE type. How can I build a string out of this data?
The goal is to get the NMEA string line from the GPS module.
How is the interactive Forth shell handled? The GPS module is soldered to the RX/TX lines of the Propeller.
So it shares the same serial lines with the shell serial input. Can the Forth shell interactive mode be switched off?
Now I want to use a GPS receiver module and get the NMEA sentences over the serial port.
Is there already a NMEA receive module for Tachyon somewhere?
I tried to get the serial data with some:
Where "serb" is of BYTE type. How can I build a string out of this data?
The goal is to get the NMEA string line from the GPS module.
How is the interactive Forth shell handled? The GPS module is soldered to the RX/TX lines of the Propeller.
So it shares the same serial lines with the shell serial input. Can the Forth shell interactive mode be switched off?
Sorry for asking some newbie stuff...
Welcome to the forum Jay!
Obviously to build a string you need to string bytes together, one after another, you can't do it with one byte. Your input routine should have a byte array that it can write the next character into and also a write index and the termination of that inputting is when it detects a CR. It's a good idea to write a null to the end of this input as it can be treated just like a string. That bit of code is fairly trivial and normally a good exercise for a beginner so if I give you an example then it's up to you to understand it and make any changes you need to make.
There is also no problem with using the console serial for this except of course how do you interact with it and debug it then? So I recommend tying the GPS transmit to another port through a 10K resistor (in case it's a 5V signal) and just use SERIN. Remember that the ! word means it stores a long, whereas you want to store a byte at a time so of course you need C! instead (it represents "character store").
Now it'll be interesting to see how you go with Mousey so please keep us updated, not just with the problems, but with the wins.
BTW: Choose "Go Advanced" in the forum buttons and change the thread title to "Mousey II" or something, and you can also even edit your first post.
#4800 SERBAUD --- set default baud rate for GPS
#84 BYTES gpsbuf --- NMEA sentence max is 80 visible characters
#P0 == rxdgps --- define out GPS receive port
pub GETGPS ( -- array ) gpsbuf BEGIN 0 OVER C! rxdgps SERIN DUP $0D <> WHILE OVER C! 1+ REPEAT DROP gpsbuf ;
GETGPS PRINT$
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 ok
Just a couple of gotcha's here...I've been looking into NMEA interface for a bit now, but on a back burner. Note that NMEA sentences permit nulls in the sentences. Peter's first suggestion (the thread MJB referenced) doesn't handle these nulls well, placing data in the wrong memory locations.
Just a couple of gotcha's here...I've been looking into NMEA interface for a bit now, but on a back burner. Note that NMEA sentences permit nulls in the sentences. Peter's first suggestion (the thread MJB referenced) doesn't handle these nulls well, placing data in the wrong memory locations.
That's not really a problem as nulls are filtered out in normal KEY input so it doesn't matter how many you get, they just don't appear in a normal ASCII stream in Tachyon. Yes it is possible to read a null if you wanted to but the only reason why is because it would be part of a file transfer protocol or something. So don't worry about those nulls, I assure you, this NMEA stuff is child's play, there are so many ways to do it, and you can have fun with all of them. The thing is not to hold back from doing anything because of overthinking, there's enough of that on this forum!
That's not really a problem as nulls are filtered out in normal KEY input so it doesn't matter how many you get, they just don't appear in a normal ASCII stream in Tachyon. Yes it is possible to read a null if you wanted to but the only reason why is because it would be part of a file transfer protocol or something. So don't worry about those nulls, I assure you, this NMEA stuff is child's play, there are so many ways to do it, and you can have fun with all of them. The thing is not to hold back from doing anything because of overthinking, there's enough of that on this forum!
LONG lat,lon,alt
LONG sum,horz,sats,fix,utc
: S NEGATE
: N lat ! ;
: W NEGATE
: E lon ! ;
: M alt ! ;
: ?GPS word C@ "*" = IF word 1+ NUMBER DROP sum ! THEN ;
: &GPGGA horz ! sats ! fix ! utc ! prompt W~ ;
: GPGGA DECIMAL ' ?GPS unum W! ' &GPGGA prompt W! ; IMMEDIATE
"," delim C!
\ our sample data
GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
\ report what we found
lat @.
lon @.
alt @.
sum @.
horz @.
sats @.
fix @.
utc @.
{ which looks like this:
lat @. 4807038 ok
lon @. 1131000 ok
alt @. 469 ok
sum @. 47 ok
horz @. 9 ok
sats @. 8 ok
fix @. 1 ok
utc @. 123519 ok
}[FONT=courier new]
[/FONT]
With this strategy (first suggestion from the previous thread), &GPGGA wierds out when one of the data fields is blank/NULL, such as "$GPGGA,123519,,,,1,08,0.9,545.4,M,46.9,M,,*47"
...Of course, this isn't a perfect NMEA sentence but is similar to what the GPS will send when it hasn't yet calculated a location fix. "Full postfix" treatment isn't possible while $GPGGA is still interpreted as $A, like #PIN4 is interpreted as #4.
In $GPGGA, there are two fields (altitude / height above geoid) which are measured in Meters, but the postfix above treats any data followed by "M" as altitude
--I've been mulling around with the idea of buffering an NMEA sentence at 4800 baud, stripping the "$" from the sentence identifier, replacing ",," with ",0," and then resending the corrected stream at 19200 baud, and handling the sentence postfix. Resending at 19200 baud should be able to fit between the sentences if your GPS is sending $GPGGA, $GPRMC, $GPGSA and $GPGSV at 1Hz. Certainly not the most elegant strategy, but seems to be a small-space solution to the above mentioned issues, within the small part of my world I control.
Peter's postfix is certainly the fastest and most code-efficient approach, but has a couple of issues off the shelf. I'm certainly looking for the fastest route from what the GPS actually sends to what he initially proposed. I'm not trying to criticize, but want to keep jay-t (a newbie like me) from pulling his hair out over a couple of critical and already identified issues. I thought I was doing good with a three-page method to accomplish what Peter came so close to accomplishing in ten lines.
With this strategy (first suggestion from the previous thread), &GPGGA wierds out when one of the data fields is blank/NULL, such as "$GPGGA,123519,,,,1,08,0.9,545.4,M,46.9,M,,*47"
...Of course, this isn't a perfect NMEA sentence but is similar to what the GPS will send when it hasn't yet calculated a location fix. "Full postfix" treatment isn't possible while $GPGGA is still interpreted as $A, like #PIN4 is interpreted as #4.
In $GPGGA, there are two fields (altitude / height above geoid) which are measured in Meters, but the postfix above treats any data followed by "M" as altitude
--I've been mulling around with the idea of buffering an NMEA sentence at 4800 baud, stripping the "$" from the sentence identifier, replacing ",," with ",0," and then resending the corrected stream at 19200 baud, and handling the sentence postfix. Resending at 19200 baud should be able to fit between the sentences if your GPS is sending $GPGGA, $GPRMC, $GPGSA and $GPGSV at 1Hz. Certainly not the most elegant strategy, but seems to be a small-space solution to the above mentioned issues, within the small part of my world I control.
Peter's postfix is certainly the fastest and most code-efficient approach, but has a couple of issues off the shelf. I'm certainly looking for the fastest route from what the GPS actually sends to what he initially proposed. I'm not trying to criticize, but want to keep jay-t (a newbie like me) from pulling his hair out over a couple of critical and already identified issues. I thought I was doing good with a three-page method to accomplish what Peter came so close to accomplishing in ten lines.
Since the KISS just uses the comma as it would a space then it's just empty space to Forth but there's an easy fix for the empty CSV value problem and as always it's only a couple of lines of code if that. This little routine should work as it remembers the last character and checks for a double comma in which case it returns with a "0" instead and forces the second comma back into the stream with a KEY!.
BYTE lastgps
pub GPSKEY
rxdgps SERIN DUP "," = lastgps C@ "," = AND --- is this the second comma?
IF KEY! "0" THEN --- force it for next time but return a 0 now
DUP lastgps C!
;[/FONT]
pub aa ( -- ) \ test
$CC DUP . CR
#8 FOR
DUP 1 AND .
#1 SHR
NEXT
\ DROP
CR
. CR
;
pub ab ( -- ) \ test
$CC DUP . CR
#8 FOR
DUP 1 AND .
#1 SHR
NEXT
DROP
CR
. CR
;
.VER
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
ok
aa CC
00110011
0
ok
ab CC
00110011
1
ok
Why is the last print giving a "0" in aa, and giving a "1" in ab?
I would expect the stack to be empty.
pub aa ( -- ) \ test
$CC DUP . CR
#8 FOR
DUP 1 AND .
#1 SHR
NEXT
\ DROP
CR
. CR
;
pub ab ( -- ) \ test
$CC DUP . CR
#8 FOR
DUP 1 AND .
#1 SHR
NEXT
DROP
CR
. CR
;
.VER
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
ok
aa CC
00110011
0
ok
ab CC
00110011
1
ok
Why is the last print giving a "0" in aa, and giving a "1" in ab?
I would expect the stack to be empty.
These two definitions can be reduced to simple one liners like this:
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR
The trouble is that at the end of the second test you are dropping the remainder of $CC which is now 0 off the stack and telling Forth to print a number from an "empty" stack except there is always a "top of stack". If I paste in these one liners this is what I get. $CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR CC
00110011
0
ok
ok $CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR CC
00110011
1
ok
Now before I run it I pad the stack with two numbers 12 34 then do it again I get: $CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR CC
00110011
0
ok
ok $CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR CC
00110011
34
ok
So you can see there is nothing mysterious it's just you are operating outside of defined parameters and I forego a lot of this "stop everything and report error code mumbo-jumo" stuff which while fine on the testing bench only disrupts a system which could otherwise continue operating normally without downtime.
TIP: There is never a need to prefix a # to single digits 0 to 9 as these will always be decimal but as a tip you could leave your system in DECIMAL mode and only prefix the hex and binary numbers. Also 2/ is more efficient than 1 SHR.
Also there is no difference in TF interactive mode and compile mode, it always compiles, so you can use your loops and conditionals without having to create a definition or use "special" forms of those words as is common in traditional Forths.
Thanks for the 12 34, now I can test my pub's.
When I use . on the terminal with nothing on the stack I get -1 back, I understand this. But when I use . in my pub same condition, I get 1 back, this confused me a bit.
With 12 34 I am back on track.
But the next little pub confuses me a bit.
pub ad CR 0 DO I . ." " LOOP CR ;
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Clock frequency = 80,000,000
NAMES: $5C89...745E for 6101 (3715 bytes added)
CODE: $0000...34EC for 7447 (7212 bytes added)
CALLS: 0450 vectors free
RAM: 10141 bytes free
MODULES LOADED:
18C0: EXTEND.fth Primary extensions to TACHYON kernel - 141027-1000
BOOT: EXTEND.boot
----------------------------------------------------------------
pub ad CR 0 DO I . ." " LOOP CR ; ok
ok
15 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14
ok
16 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F
ok
17 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16
ok
All in hex, 15 and 17 I understand, but 16 I do not.
Am I too sluggish on the uptake.
I can reproduce it.
interrestingly
26 . 26 ok is correct
just trying to figure out the kernel code to see how this can happen
got it
16 is one of the fast constants like -1, 0,1 ... 8,16, BL for 32, FALSE = 0, OFF = 0, ON = -1, TRUE = -1
it is intercepted and handled independently of BASE setting.
so definitely 16 independently of the base set is #16 $10
so better set default base to 10 and mark all hex numbers clearly with $.
maybe it should be renamed to #16 - can still be a fast constant.
DECIMAL \ sets input and output base
15 . 15 ok
16 . 16 ok
#16 . 16 ok
$10 . 16 ok
.S \ prints the stack in both DECIMAL and HEX always good to cross check
Thanks for the 12 34, now I can test my pub's.
When I use . on the terminal with nothing on the stack I get -1 back, I understand this. But when I use . in my pub same condition, I get 1 back, this confused me a bit.
With 12 34 I am back on track.
But the next little pub confuses me a bit.
pub ad CR 0 DO I . ." " LOOP CR ;
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Clock frequency = 80,000,000
NAMES: $5C89...745E for 6101 (3715 bytes added)
CODE: $0000...34EC for 7447 (7212 bytes added)
CALLS: 0450 vectors free
RAM: 10141 bytes free
MODULES LOADED:
18C0: EXTEND.fth Primary extensions to TACHYON kernel - 141027-1000
BOOT: EXTEND.boot
----------------------------------------------------------------
pub ad CR 0 DO I . ." " LOOP CR ; ok
ok
15 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14
ok
16 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F
ok
17 ad
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16
ok
All in hex, 15 and 17 I understand, but 16 I do not.
Am I too sluggish on the uptake.
15 . 15 ok
16 . 10 ok
17 . 17 ok
Maybe you better check all sequences up to 4294967296? Do you want to be a beta tester?
16 is one of the predefined fast constants just like 0 1 2 etc but unlike 0 to 9 which are the same in hex or decimal there is this little problem that you have come across. But I had decided by then when I had added it to the kernel that my hex numbers were all going to be prefixed to avoid any confusion at all. So if I meant 16 in hex then I would say $16 and indeed it's a force of habit now that all hex numbers I type in are prefixed.
Now I have an additional leaning towards leaving the system in decimal mode as I can easily control radix of input and output without affecting everything globally. The variable base is a very nice feature of Forth but it also creates problems too which is why I have ways to force recognition of numbers. So my source code will always have hex numbers prefixed with $ but I have used # to make sure my decimal numbers are decimal but maybe if I leave the default base set to decimal then these won't need to be prefixed and if you see 16 just as it is, then it really is 16 decimal.
As for printing numbers there are plenty of support words for that including the very flexible .NUM word which accepts a control word to specify digits, base, separators, leading blanks or zeros etc.
Actually that brings up another point, the traditional dot that's used in Forth to print a number is a bit too terse in source code form as a tiny dot gets lost whereas during debug it's quick and easy. That's why I have aliases for many of these words such as PRINT and PRINT" so that these words stand out clearly in source code rather than . and ." so perhaps .NUM needs an alias too, PRINTNUM.
The problem: A fully loaded networked system only has a couple of KBs of RAM left for an application even after reclaiming memory. Although most of the dictionary is not needed at runtime, it is needed to compile the application, so the 10K it takes up cannot be freed before an application is compiled which results in a catch-22 situation.
The proposed solution: Move the dictionary out of RAM into a file in such a way that words can be found quickly and easily, preferably at normal source code compile speeds.
The result: The dictionary optimization code is up and running and fully integrated into Tachyon now. It sorts and stores the dictionary into indexed sectors so that for any word there is an indexed sector where the valid word will be found along with it's attribute and code header. The 7-bit index is generated from 5 lsbs of the first character plus 2 lsbs of it's count and this 7-bit index is used to open a 512-byte sector within the 64KB WORDS.DCT file.
This method is fast and even if it needed to read in a sector for every word it only takes around 3ms per word. However a cache is implemented which pushes the latest header into the start of the cache including any matches in the cache itself to keep it fresh. So before a sector is indexed and read in, the cache is searched for a match first, which after it starts to fill up will end up scoring more and more hits, especially since it keeps it's hits fresh. The oldest words that get pushed or partially pushed off (variable lengths) the end of the cache become invalid anyway due to a null termination area.
This works but that still leaves the problem of adding new words to the dictionary easily. Since the normal Tachyon dictionary grows downwards in memory it becomes a simple matter to say that the start of the cache is the default start of the dictionary. So instead of searching the "cache" Tachyon will use it's normal method of searching from "names" (pointer to the dictionary) and initally it's the same thing. Now as new defintions are created they are prepended to the "dictionary" which is now a combination of the new words growing downwards in memory but still cascading into the cache. It just works, and really well at that.
To test it out properly I loaded my datalogging application onto a full system with EASYFILE and EASYNET running. Normally even after reclaiming all the private headers (about >2KBs worth) there is no room left after this and no easy way to add more to it. Now I leave all the private headers intact, have the application fully loaded in and there is around 6K free.
You would also think that looking up each word from SD might be slow but the combination of indexed sectors, cache, and dictionary let me load my application in without any extra delays.
This paves the way for adding even more features to what is essentially a little 32KB microcontroller. It's now possible to include VGA buffers and many other things including the assembler etc.
Out of interest here's a MAP listing with comments which prints out a packed byte for every xN bytes, some "bytes" are comprised of 16 or 256 bytes etc. However the main area of interest is in red where even with the whole dictionary intact and networking and application we still have almost 6K free.In terms of Forth code that's a whole continent.
So do we need a "--- convert the dictionary in RAM to a sorted and indexed dictionary in SDFS in the WORDS.DCT file (128K)" or a 64KB file as stated in this post.
Also at the bottom of SDWORDS.fth you talk about loading all the goodies except the app and running SDWORDS then initializing with !SDWORDS.
So step one: create a 64 or 128K blank file named WORDS.DCT on the SDCard
Two: load up tachyon the way you want your kernel upto the application you are building/working on.
Three: Run SDWORDS from the SDWORDS.fth module
Four: BACKUP
Five: initialize your rebooted Tachyon sessions with !SDWORDS to use the dictionary interactively and/or compile your app
Six: you are able to simply forget your app or reboot for development ease
Seven: backup again after your app is ready
Eight: now you have so much code space you are bound to have even more fun
So do we need a "--- convert the dictionary in RAM to a sorted and indexed dictionary in SDFS in the WORDS.DCT file (128K)" or a 64KB file as stated in this post.
Also at the bottom of SDWORDS.fth you talk about loading all the goodies except the app and running SDWORDS then initializing with !SDWORDS.
So step one: create a 64 or 128K blank file named WORDS.DCT on the SDCard
Two: load up tachyon the way you want your kernel upto the application you are building/working on.
Three: Run SDWORDS from the SDWORDS.fth module
Four: BACKUP
Five: initialize your rebooted Tachyon sessions with !SDWORDS to use the dictionary interactively and/or compile your app
Six: you are able to simply forget your app or reboot for development ease
Seven: backup again after your app is ready
Eight: now you have so much code space you are bound to have even more fun
Is this about right?
I will probably try to automate this as easily as possible but for the moment I am invoking operations manually to check it all. Right now I'm seeing if I can load up the full EXTEND.fth etc, compact the dictionary after EASYFILE, load EASYNET, and compact again. In fact I may just create the word COMPACT to do this automatically so it works out whether it's a new dictionary or an addition etc.
So steps at present are:
1. Load in SDWORDS.fth on a system that has EASYFILE already loaded.
2. Ensure you have a file called WORDS.DCT with at least 64KB available (only uses 64KB)
3. Run SDWORDS which will populate WORDS.DCT sectors with those words as they are indexed
4. When you want to release the old dictionary in RAM then run !SDWORDS which will init the cache, reset the dictionary to the start of cache, and revector the SEARCH method.
5. BACKUP after !SDWORDS if you want to lock all this in now. Run .STATS to see how much RAM is now available.
6. Load app normally and this can also be forgotten normally as well. Only way to forget compacted stuff at present is to do a COLD and start again.
7. Forth Freaking Frolicking Fun
Newer method may just use the one word COMPACT and then manually BACKUP in case there is an ooops!.
BTW, I still have to add an FCREATE to EASYFILE to allow new files to be created in the system although I had been trying to avoid any FAT32 housekeeping on the embedded side previously.
I will probably try to automate this as easily as possible but for the moment I am invoking operations manually to check it all. Right now I'm seeing if I can load up the full EXTEND.fth etc, compact the dictionary after EASYFILE, load EASYNET, and compact again. In fact I may just create the word COMPACT to do this automatically so it works out whether it's a new dictionary or an addition etc.
<snip>
Newer method may just use the one word COMPACT and then manually BACKUP in case there is an ooops!.
Just created COMPACT and tested it all out which is all you ever need to worry about for now and you can even compact the dictionary as you go as it will detect whether it is a new or additional operation.
To test this out I loaded a full EXTEND.fth so it included the whole kit and kaboodle, loaded EPRINT, my hardware headers, SDCARD, EASYFILE and without trying to reclaim any headers as there is no need to I then loaded SDWORDS and had 1.4K free.
Ran COMPACT and ended up with 9.3K free.
Added W5200 and EASYNET and ended up with 1.7K
Ran COMPACT and finally still have 3.9K free even with all the EXTEND fluff and ALL the headers intact!
LISTWORDS Mounted SD Card
Media mounted as 71CC.0203 mkfs.fat WIDGET FAT32 Cluster size = 4,096 Sectors = 4,512
Just created COMPACT and tested it all out which is all you ever need to worry about for now and you can even compact the dictionary as you go as it will detect whether it is a new or additional operation.
To test this out I loaded a full EXTEND.fth so it included the whole kit and kaboodle, loaded EPRINT, my hardware headers, SDCARD, EASYFILE and without trying to reclaim any headers as there is no need to I then loaded SDWORDS and had 1.4K free.
Ran COMPACT and ended up with 9.3K free.
Added W5200 and EASYNET and ended up with 1.7K
Ran COMPACT and finally still have 3.9K free even with all the EXTEND fluff and ALL the headers intact!
if I remember correctly there are 1k call vectors in the XCALL, YCALL, ZCALL, WCALL tables each 256 entries.
Aren't we at the limit now ?
Yes, you noticed, but it's all being taken care of. One of the main reasons I used vectors in the first place was so that Spin tool compiled bytecode kernel would play nice with bytes but not words, otherwise every single word would have to be broken up into high and low bytes with offsets etc. Doable, but such a pain. Secondly, using vectors meant that we could reduce a high-level call from 3 bytes (opcode+address) to 2 bytes where one of four opcodes each picks one of 256 16-bit vectors from the table.
So there is nothing to stop us from using 3 byte calls at a higher level on top of the kernel. I have implemented the CALL16 and getting back to modifying the BCOMPILE word so that it if it runs out of vectors for new words it will just compile a CALL16 opcode followed by the absolute address of the function it is calling. No big deal and at this level there is no real difference in code size as it is only the references to new unvectored words that take an extra byte.
Sometimes I feel like Harper from Andromeda, yeah baby, well at least when it all comes together and it works. When it doesn't then sometimes I feel like Harper from Andromeda.......IYKWIM
Since the RUNMOD function only requires the start address and count it is easy enough to have a blank area or any other area for that matter such as the tables in ROM but otherwise just feel free to use the BUFFERS area or part thereof.
Initially you could: BUFFERS $800 ERASE BUFFERS pxlbytes + pxlbytes $FF FILL
To blank all the LEDs: BUFFERS pxlbytes RUNMOD
To light em up: BUFFERS pxlbytes + pxlbytes RUNMOD
In fact rather than calling RUNMOD directly you should really have this:
pub SHOW ( address -- ) pxlbytes RUNMOD #50 us ;
So BLANK becomes pub BLANK BUFFERS SHOW ;
Works fabulously! In efforts to be constantly wary to save vectors and bytes, I found an issue I don't understand...Can you see my error?
pub BUF ( -- ) wsdata wsbytes + ;
pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;
pub BLANK ( -- ) BLANKBUFF #10 us BUF SHOW ;
works perfectly, while
pub BLANK ( -- )
pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;
#10 us BUF SHOW ;
You can also define storage areas as constants with ORG such as:
BUFFERS ORG
pxlbytes DS pxldata
1 DS tempgrn
and so on to keep it all together without worrying about slamming into code.
Your mini tutorial was helpful, but one added question: Is there a way to keep track of what part of the buffers has been claimed? Since my data is started at BUFFERS, I need to increment the "buffer" area to keep from wiping out the data I want preserved...OR is there a better way to ORG so that I don't run into the same issue where data is being stored in code space?
Works fabulously! In efforts to be constantly wary to save vectors and bytes, I found an issue I don't understand...Can you see my error?
pub BUF ( -- ) wsdata wsbytes + ;
pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;
pub BLANK ( -- ) BLANKBUFF #10 us BUF SHOW ;
works perfectly, while
pub BLANK ( -- )
pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;
#10 us BUF SHOW ;
does not.
Your mini tutorial was helpful, but one added question: Is there a way to keep track of what part of the buffers has been claimed? Since my data is started at BUFFERS, I need to increment the "buffer" area to keep from wiping out the data I want preserved...OR is there a better way to ORG so that I don't run into the same issue where data is being stored in code space?
It looks like you are over-thinking things a little as ORG and DS etc look after that stuff for you. The thing is that you can use this in one part of the code and then use it later and it still points to the next available area. But if you really want to know where it is just look in the org variable (lower-case). But you don't need to keep setting ORG, normally just once is fine.
The 10 us is redundant here, it was used at the end of a transmission to ensure a minimum gap period, that's all.
Now, it normally helps if I can see all your code rather than snippets but I'm wondering what you are trying to do by nesting a definition within a definition? Perhaps you copy and pasted without removing the ; after the FILL ?
pub BLANK ( -- )
[COLOR=#ff0000]pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;[/COLOR]
#10 us BUF SHOW ;
It looks like you are over-thinking things a little as ORG and DS etc look after that stuff for you. The thing is that you can use this in one part of the code and then use it later and it still points to the next available area. But if you really want to know where it is just look in the org variable (lower-case). But you don't need to keep setting ORG, normally just once is fine.
The 10 us is redundant here, it was used at the end of a transmission to ensure a minimum gap period, that's all.
Now, it normally helps if I can see all your code rather than snippets but I'm wondering what you are trying to do by nesting a definition within a definition? Perhaps you copy and pasted without removing the ; after the FILL ?
pub BLANK ( -- )
[COLOR=#ff0000]pub BLANKBUFF ( -- ) BUF wsbytes $00 FILL ;[/COLOR]
#10 us BUF SHOW ;
Doesn't nesting like this save memory? Obviously not a lot in this case, but I'm trying to be as efficient as possible from the start so as I learn and as my projects become more complex, I've developed good habits..Nesting is, of course only after I've tweaked and verified, when I'm cleaning up and commenting my code for later interactions.
BUFFERS $800 ERASE BUFFERS pxlbytes + pxlbytes $FF FILL
it blanked out the data I was trying to preserve...I have packaged the whole thing into a module and have BACKUP'd the Prop. BUFFERS . returns the address of my ORG'd data ( wsgrn . = 7500, BUFFERS . = 7500)
...Is BUFFERS ORG the wrong way to setup the variables?
I'm also not seeing the advantage of ERASE then FILL. Is there one I don't see other than cleaning up pseudo-random data for possible later use?
Doesn't nesting like this save memory? Obviously not a lot in this case, but I'm trying to be as efficient as possible from the start so as I learn and as my projects become more complex, I've developed good habits..Nesting is, of course only after I've tweaked and verified, when I'm cleaning up and commenting my code for later interactions.
BUFFERS $800 ERASE BUFFERS pxlbytes + pxlbytes $FF FILL
it blanked out the data I was trying to preserve...I have packaged the whole thing into a module and have BACKUP'd the Prop. BUFFERS . returns the address of my ORG'd data ( wsgrn . = 7500, BUFFERS . = 7500)
...Is BUFFERS ORG the wrong way to setup the variables?
I'm also not seeing the advantage of ERASE then FILL. Is there one I don't see other than cleaning up pseudo-random data for possible later use?
The semicolon just after the fill compiles as an EXIT so the code that you have after that will not execute plus this is something you never do in any Forth although Tachyon does allow multiple entry points.
[COLOR=#000000][FONT=Arial]pub BLANK ( -- ) [/FONT][/COLOR]
[COLOR=#ff0000][FONT=Arial] pub BLANKBUFF ( -- ) [/FONT][/COLOR]
[COLOR=#ff0000][FONT=Arial] BUF wsbytes $00 FILL ;[/FONT][/COLOR][COLOR=#000000][FONT=Arial] <---------code will exit here because of the semicolon (it's an EXIT) [/FONT][/COLOR]
[COLOR=#000000][FONT=Arial] #10 us BUF SHOW ; <------- the code here never gets executed[/FONT][/COLOR]
Also remember that $00 compiles as a 2 byte opcode [BYTE] [00] whereas 0 is an opcode and compiles as a fast single byte opcode [0]. In this case it is neither here nor there but be aware of it if.
Once again I can't help you too much when I can only look through a keyhole, just post all the code. For instance you have wsbytes specifying the number of bytes for wsdata but I don't know what value that is. Then you have pxlbytes which is another value I don't know. However BUFFERS $800 ERASE is going to wipe everything for sure, is that what you want?
Try putting things like BUFFERS $800 ERASE on one line by itself and give it a comment so that you at least will know what you are trying to do, then put that BUFFERS pxlbytes + pxlbytes $FF FILL on it's own line with a comment. Be methodical and try talking to yourself as if you are explaining what you are doing to someone who is trying to follow along. As you verbalize your thoughts you will do as many of us have done and go "you idiot" or "of course, why didn't I see that before" etc.
This particular thread has grown so much with over 140,000 view and 1700 posts and there is so much buried in here that I always wanted to save this thread locally so I could search it easily. So I installed the re-pagination add-on for FF which lets me right click the "next" or "page 2" in the thread and re-paginate it "load all". After a minute or so all the posts are visible so I saved this as a html with sub-folder but also pasted the text of it into this handy zip file:
I wrote a little program for my mouse bot.
The bot should search light with it's phototransistors. I did a run with motor driver battery unplugged:
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Clock frequency = 80,000,000
NAMES: $5B01...745E for 6493 (102 bytes added)
CODE: $0000...3DBF for 9314 (577 bytes added)
CALLS: 0420 vectors free
RAM: 7490 bytes free
jimmy light measure
turn right
driveahead
driveahead
driveahead
...
It just repeats the "driveahead" message over and over again. Without jumping back to the BEGIN statement.
Here is the program:
TACHYON
: jimmybot.FTH ." Jimmy the mouse bot main program " ;
[~
{
The main control program for Jimmy the mouse bot.
}
DECIMAL
LONG irleft
LONG irright
LONG irfront
LONG turned
LONG lightsensleft
LONG lightsensright
: jimmy
initpwm
\ test IR LEDs
tirfront
tirleft
tirright
\ test motors
driveahead
#500 ms
drivestop
#500 ms
\ beeperok
#5000 ms
BEGIN
sensorfront irfront ! \ store sensor data
#5 ms
sensorleft irleft !
#5 ms
sensorright irright !
0 turned !
irfront @
-1 = IF
\ front free
." light measure " CR
\ measure light on left and right side
lightleft lightsensleft !
lightright lightsensright !
lightsensleft @ lightsensright @
< IF
\ left side is brighter, turn left
irleft @
-1 = IF
." turn left " CR
drivestop
#1000 ms
turnleft
#1000 ms
drivestop
ELSE
\ left side blocked, go backwards and right
." turn backwards right " CR
drivestop
#1000 ms
drivebackw
#500 ms
turnright
#1000 ms
drivestop
THEN
ELSE
\ right side is brighter, turn right
irright @
-1 = IF
." turn right " CR
drivestop
#1000 ms
turnright
#1000 ms
drivestop
ELSE
\ left side blocked, go backwards and left
." turn backwards left " CR
drivestop
#1000 ms
drivebackw
#500 ms
turnleft
#1000 ms
drivestop
THEN
THEN
sensorfront irfront ! \ store sensor data
irfront @
-1 = IF
\ front free, drive ahead
." driveahead " CR
driveahead
#500 ms
drivestop
THEN
ELSE
\ front blocked somewhere
irfront @ irleft @ OR
-1 = IF
irleft @
-1 = IF
." backwards and left " CR
drivestop
#1000 ms
drivebackw
#500 ms
turnleft
#1000 ms
drivestop
-1 turned !
THEN
THEN
irfront @ irright @ OR
-1 = IF
irright @
-1 = IF
." backwards right " CR
drivestop
#1000 ms
drivebackw
#500 ms
turnright
#1000 ms
drivestop
-1 turned !
THEN
THEN
turned @
0 = IF
\ no way to turn, drive backwards
." backwards " CR
drivestop
#1000 ms
drivebackw
#500 ms
drivestop
THEN
THEN
AGAIN
;
]~
END
It should print out the "light measure" message after "driveahead" by jumping back to "BEGIN", but it never does this.
Maybe I messed up something with my IF/ELSE/THEN stuff?
Many years ago I designed and built a wave player using a 20x4 LCD and PS/2 keypad that played files from a FAT16 SD card. The whole thing was written in Spin and PASM of course, as you do, well, at least back then.
Then I developed Tachyon and one of the early snippets of code I wrote was for a bufferless wave player that read continuously from the SD card even though I hadn't written any filesystem stuff yet. Over the the weekend I just played with playing back files in a few different ways and I found that I could just manage 44,100Hz sample rate playback totally in Tachyon. Anyway I've tidied it up a bit and actually put in a small RUNMOD so I can push it harder and which grabs one sample, scales it, add bias, and feeds it out. So except for those dozen lines of PASM in the kernel, the rest is totally Forth.
This code here will play a file in the background, and you can PAUSE or CONT or FADE or vary pitch interactively. Type PLAY <thisfile> to start play or add a small macro like TRACK so you can say 123 TRACK and it will play TRACK123.WAV. The full kit may come later but this is fun to play with. BTW, I use Audacity and convert MP3 stereo to mono, normalise, and export as a MS WAVE file.
Code size = 735 bytes
Names size = 335 bytes
[FONT=courier new]TACHYON [~
DECIMAL
FORGET WAVEPLAY.fth
pub WAVEPLAY.fth ." Tachyon Wave file player - 141107-1500 " ;
IFNDEF EASYFILE.fth
CR ." !!! This module requires EASYFILE.fth !!! "
!!!
}
--- For cards larger than 4GB a sector address needs to be used instead of FILE@
IFNDEF FSECT@
pub FSECT@ ( -- sect ) file @FILE @ ;
}
--- define 2 contiguous audio buffers which are fed from the SDFS
--- Use file 2 & 3
2 FILE SDBUF == ABUF
ABUF == BUF1
BUF1 BLKSIZ + == BUF2
ABUF == WBUF --- Let player use it's own buffer constant which can be changed at runtime
0 FILE
--- Define our I/O pins which are fed to RC filters for audio, I use 220R + 100nF
#P21 == LEFT
#P20 == RIGHT
--- default sampling rate
44100 == RATE
--- array to hold RIFF information from WAVE file
128 BYTES riff
--- RIFF WAVE HEADER OFFSETS
riff $16 + == @channels
riff $18 + == @samplerate
riff $24 + == @chunks --- LIST INFO INAM IART chunks etc
pub .TAGS
CR ." Sample rate : " @samplerate @ $400A .NUM
CR ." Channels : " @channels W@ 1 = IF ." Mono " ELSE ." Stereo" THEN
CR ." Title : " riff $38 + PRINT$
;
LONG playfile,playsize
LONG pitch,pitch100,track
WORD pidx --- player updates it's buffer read index in this word
BYTE pflg --- next word is foreground flag
BYTE fade,pause
pub SILENT 20 fade C! ;
pub UNMUTE 0 fade C! ;
pub PITCH ( +/-% -- ) NEGATE pitch100 @ SWAP % pitch100 @ + #1100 MAX pitch ! ;
pub PAUSE pause C~~ SILENT ;
pub CONT pause C~ UNMUTE ;
--- PLAYER runs continuously in it's own cog feeding audio samples to the DAC
pub PLAYER ( runs at 44100Hz using small RUNMOD )
[WAVE] --- load PLAYER helped
RIGHT DUP PINCLR A BPIN
LEFT DUP PINCLR A APIN %00111 CTRMODE --- setup audio output
fade 0 COGREG! --- pass pointer to fade control
pitch 1 COGREG! --- allow pitch to vary
pidx 2 COGREG! --- let runmod update it's read index
CLKFREQ RATE / DUP pitch ! DUP pitch100 ! DELTA --- setup WAITCNT sample rate
BEGIN
WBUF $400 ADO RUNMOD 2 +LOOP --- continously output 512x2 sample buffer
AGAIN
;
pub FeedBuffer ( buf -- )
playfile @ SWAP SDRD DROP
playfile ++ playsize --
;
--- foreground task - feeds wave buffer from file as needed
pub PLAYPOLL
playsize @ pause C@ NOT AND
IF
pidx W@ BUF2 > --- if buffer read count < BLKSIZ then 1st buffer can be updated
IF pflg C@ 1 <> IF BUF1 FeedBuffer 1 pflg C! THEN
ELSE pflg C@ 2 <> IF BUF2 FeedBuffer 2 pflg C! THEN
THEN
ELSE
20 fade C!
THEN
;
pub PLAY ( <name> -- ) IMMEDIATE
?MOUNT
0 FILE
[COMPILE] FOPEN
pub PLAYFILE --- play the currently open file
PAUSE
FSECT@ 0EXIT
FSECT@ playfile ! FSIZE@ 9 SHR playsize !
' PLAYER 6 RUN --- make sure PLAYER is running
BEGIN pidx W@ BUF2 = UNTIL --- don't load anything until player is just using 2nd buffer
BUF1 FeedBuffer 1 pflg C! --- and load 1st buffer now
ABUF riff #128 CMOVE --- save header
$8000 ABUF W! ABUF ABUF 2+ $FE CMOVE --- insert silence over WAV header in first 128 bytes
' PLAYPOLL keypoll W! --- polling will fill audio buffers as needed
.TAGS --- display some metatag info
BEGIN pidx W@ BUF1 #128 + = UNTIL --- let it play from this position to skip header
CONT --- continue playing
PLAYPOLL --- replenish buffers
;
--- application specific
pub TRACK ( nnn -- )
DUP track !
<# # # # #> " TRACK000.WAV" SWAP OVER 4 + 3 CMOVE FOPEN$ IF PLAYFILE THEN
;
pub EVAC 911 TRACK ;
]~
END
[/FONT]
Many years ago I designed and built a wave player using a 20x4 LCD and PS/2 keypad that played files from a FAT16 SD card. The whole thing was written in Spin and PASM of course, as you do, well, at least back then.
Then I developed Tachyon and one of the early snippets of code I wrote was for a bufferless wave player that read continuously from the SD card even though I hadn't written any filesystem stuff yet. Over the the weekend I just played with playing back files in a few different ways and I found that I could just manage 44,100Hz sample rate playback totally in Tachyon. Anyway I've tidied it up a bit and actually put in a small RUNMOD so I can push it harder and which grabs one sample, scales it, add bias, and feeds it out. So except for those dozen lines of PASM in the kernel, the rest is totally Forth.
This code here will play a file in the background, and you can PAUSE or CONT or FADE or vary pitch interactively. Type PLAY <thisfile> to start play or add a small macro like TRACK so you can say 123 TRACK and it will play TRACK123.WAV. The full kit may come later but this is fun to play with. BTW, I use Audacity and convert MP3 stereo to mono, normalise, and export as a MS WAVE file.
Code size = 735 bytes
Names size = 335 bytes
[FONT=courier new]TACHYON [~
DECIMAL
FORGET WAVEPLAY.fth
pub WAVEPLAY.fth ." Tachyon Wave file player - 141107-1500 " ;
IFNDEF EASYFILE.fth
CR ." !!! This module requires EASYFILE.fth !!! "
!!!
}
--- For cards larger than 4GB a sector address needs to be used instead of FILE@
IFNDEF FSECT@
pub FSECT@ ( -- sect ) file @FILE @ ;
}
--- define 2 contiguous audio buffers which are fed from the SDFS
--- Use file 2 & 3
2 FILE SDBUF == ABUF
ABUF == BUF1
BUF1 BLKSIZ + == BUF2
ABUF == WBUF --- Let player use it's own buffer constant which can be changed at runtime
0 FILE
--- Define our I/O pins which are fed to RC filters for audio, I use 220R + 100nF
#P21 == LEFT
#P20 == RIGHT
--- default sampling rate
44100 == RATE
--- array to hold RIFF information from WAVE file
128 BYTES riff
--- RIFF WAVE HEADER OFFSETS
riff $16 + == @channels
riff $18 + == @samplerate
riff $24 + == @chunks --- LIST INFO INAM IART chunks etc
pub .TAGS
CR ." Sample rate : " @samplerate @ $400A .NUM
CR ." Channels : " @channels W@ 1 = IF ." Mono " ELSE ." Stereo" THEN
CR ." Title : " riff $38 + PRINT$
;
LONG playfile,playsize
LONG pitch,pitch100,track
WORD pidx --- player updates it's buffer read index in this word
BYTE pflg --- next word is foreground flag
BYTE fade,pause
pub SILENT 20 fade C! ;
pub UNMUTE 0 fade C! ;
pub PITCH ( +/-% -- ) NEGATE pitch100 @ SWAP % pitch100 @ + #1100 MAX pitch ! ;
pub PAUSE pause C~~ SILENT ;
pub CONT pause C~ UNMUTE ;
--- PLAYER runs continuously in it's own cog feeding audio samples to the DAC
pub PLAYER ( runs at 44100Hz using small RUNMOD )
[WAVE] --- load PLAYER helped
RIGHT DUP PINCLR A BPIN
LEFT DUP PINCLR A APIN %00111 CTRMODE --- setup audio output
fade 0 COGREG! --- pass pointer to fade control
pitch 1 COGREG! --- allow pitch to vary
pidx 2 COGREG! --- let runmod update it's read index
CLKFREQ RATE / DUP pitch ! DUP pitch100 ! DELTA --- setup WAITCNT sample rate
BEGIN
WBUF $400 ADO RUNMOD 2 +LOOP --- continously output 512x2 sample buffer
AGAIN
;
pub FeedBuffer ( buf -- )
playfile @ SWAP SDRD DROP
playfile ++ playsize --
;
--- foreground task - feeds wave buffer from file as needed
pub PLAYPOLL
playsize @ pause C@ NOT AND
IF
pidx W@ BUF2 > --- if buffer read count < BLKSIZ then 1st buffer can be updated
IF pflg C@ 1 <> IF BUF1 FeedBuffer 1 pflg C! THEN
ELSE pflg C@ 2 <> IF BUF2 FeedBuffer 2 pflg C! THEN
THEN
ELSE
20 fade C!
THEN
;
pub PLAY ( <name> -- ) IMMEDIATE
?MOUNT
0 FILE
[COMPILE] FOPEN
pub PLAYFILE --- play the currently open file
PAUSE
FSECT@ 0EXIT
FSECT@ playfile ! FSIZE@ 9 SHR playsize !
' PLAYER 6 RUN --- make sure PLAYER is running
BEGIN pidx W@ BUF2 = UNTIL --- don't load anything until player is just using 2nd buffer
BUF1 FeedBuffer 1 pflg C! --- and load 1st buffer now
ABUF riff #128 CMOVE --- save header
$8000 ABUF W! ABUF ABUF 2+ $FE CMOVE --- insert silence over WAV header in first 128 bytes
' PLAYPOLL keypoll W! --- polling will fill audio buffers as needed
.TAGS --- display some metatag info
BEGIN pidx W@ BUF1 #128 + = UNTIL --- let it play from this position to skip header
CONT --- continue playing
PLAYPOLL --- replenish buffers
;
--- application specific
pub TRACK ( nnn -- )
DUP track !
<# # # # #> " TRACK000.WAV" SWAP OVER 4 + 3 CMOVE FOPEN$ IF PLAYFILE THEN
;
pub EVAC 911 TRACK ;
]~
END
[/FONT]
Just un-possible, I like this form of the word, now to get something warm to drink and unpack these words. Heater's gonna go crazy, I can hear it now (WHAT ABOUT FFT)
A 1 step into 1-wire.
I was looking after a 1-wire object, but couldn't find one.
I saw something in the dropbox, but it was a mix of spin and forth.
So I started my own development of a 1-wire in forth.
Now I have it running and spitting 3 temperaturer back on me.
I have had a lot of fun, and it is almost like assembler.
Have a look at the object.
It seeks the 1-wire and returns the id numbers of the chips with crc8 check.
Then it send the temperature back on the console.
Comments
As the eternal optimist was heard saying as he plummeted from the top of the tall building "so far, so good". The SDWORDS conversion (71 bytes) just works and all the information seems to be there fine.
Typing in this definition:
pub LOOK ( <name> -- ) IMMEDIATE --- just a handy debug to dump the relevant word sector
GETWORD 1- C@++ 7 SHL SWAP C@ + 9 SHL FILE@ + $100 SD DUMP
;
then using it to have a look at a word sector:
LOOK CONNECT
0480_CA00: 07 43 4F 4E 54 45 4E 54 8A C0 C1 07 43 4F 4E 4E .CONTENT....CONN
0480_CA10: 45 43 54 8A BF A9 07 43 4F 55 4E 54 55 50 82 C2 ECT....COUNTUP..
0480_CA20: CE 07 43 4F 47 49 4E 49 54 82 C1 A7 07 43 4F 47 ..COGINIT....COG
0480_CA30: 52 45 47 40 82 C1 3F 07 43 52 45 41 54 45 24 82 [EMAIL="REG@..?.CREATE"]REG@..?.CREATE[/EMAIL]$.
0480_CA40: C1 1A 07 43 4C 4B 46 52 45 51 82 C1 09 07 43 4F ...CLKFREQ....CO
0480_CA50: 4D 50 49 4C 45 A2 C2 60 07 43 4F 47 44 55 4D 50 MPILE..`.COGDUMP
0480_CA60: 82 C2 17 07 43 4F 47 49 4E 49 54 82 C2 97 07 43 ....COGINIT....C
0480_CA70: 4F 4E 53 4F 4C 45 82 C2 4B 00 00 00 00 00 00 00 ONSOLE..K.......
0480_CA80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
So this is the sector that would be presented as the "dictionary" whenever I type in a 7 character word that starts with a capital C. The nulls at the end of the words would then be seen as an end of dictionary if there wasn't a match and it would either try to convert it to a number or fail to find the word which is normal.
I wrote a little Tachyon Forth program to make it avoid obstacles via it's IR sensors.
http://diy-2010.net/community/2014/04/05/mousey-ii-building-a-new-mousebot/
Now I want to use a GPS receiver module and get the NMEA sentences over the serial port.
Is there already a NMEA receive module for Tachyon somewhere?
I tried to get the serial data with some:
Where "serb" is of BYTE type. How can I build a string out of this data?
The goal is to get the NMEA string line from the GPS module.
How is the interactive Forth shell handled? The GPS module is soldered to the RX/TX lines of the Propeller.
So it shares the same serial lines with the shell serial input. Can the Forth shell interactive mode be switched off?
Sorry for asking some newbie stuff...
Welcome to the forum Jay!
Obviously to build a string you need to string bytes together, one after another, you can't do it with one byte. Your input routine should have a byte array that it can write the next character into and also a write index and the termination of that inputting is when it detects a CR. It's a good idea to write a null to the end of this input as it can be treated just like a string. That bit of code is fairly trivial and normally a good exercise for a beginner so if I give you an example then it's up to you to understand it and make any changes you need to make.
There is also no problem with using the console serial for this except of course how do you interact with it and debug it then? So I recommend tying the GPS transmit to another port through a 10K resistor (in case it's a 5V signal) and just use SERIN. Remember that the ! word means it stores a long, whereas you want to store a byte at a time so of course you need C! instead (it represents "character store").
Now it'll be interesting to see how you go with Mousey so please keep us updated, not just with the problems, but with the wins.
BTW: Choose "Go Advanced" in the forum buttons and change the thread title to "Mousey II" or something, and you can also even edit your first post.
#4800 SERBAUD --- set default baud rate for GPS
#84 BYTES gpsbuf --- NMEA sentence max is 80 visible characters
#P0 == rxdgps --- define out GPS receive port
pub GETGPS ( -- array ) gpsbuf BEGIN 0 OVER C! rxdgps SERIN DUP $0D <> WHILE OVER C! 1+ REPEAT DROP gpsbuf ;
GETGPS PRINT$
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 ok
http://forums.parallax.com/showthread.php/141061-TACHYON-a-very-fast-and-compact-Forth-SDFS-TELNET-FTP-WEB-servers-EMAIL?p=1253073&viewfull=1#post1253073
Just a couple of gotcha's here...I've been looking into NMEA interface for a bit now, but on a back burner. Note that NMEA sentences permit nulls in the sentences. Peter's first suggestion (the thread MJB referenced) doesn't handle these nulls well, placing data in the wrong memory locations.
That's not really a problem as nulls are filtered out in normal KEY input so it doesn't matter how many you get, they just don't appear in a normal ASCII stream in Tachyon. Yes it is possible to read a null if you wanted to but the only reason why is because it would be part of a file transfer protocol or something. So don't worry about those nulls, I assure you, this NMEA stuff is child's play, there are so many ways to do it, and you can have fun with all of them. The thing is not to hold back from doing anything because of overthinking, there's enough of that on this forum!
With this strategy (first suggestion from the previous thread), &GPGGA wierds out when one of the data fields is blank/NULL, such as "$GPGGA,123519,,,,1,08,0.9,545.4,M,46.9,M,,*47"
...Of course, this isn't a perfect NMEA sentence but is similar to what the GPS will send when it hasn't yet calculated a location fix. "Full postfix" treatment isn't possible while $GPGGA is still interpreted as $A, like #PIN4 is interpreted as #4.
In $GPGGA, there are two fields (altitude / height above geoid) which are measured in Meters, but the postfix above treats any data followed by "M" as altitude
--I've been mulling around with the idea of buffering an NMEA sentence at 4800 baud, stripping the "$" from the sentence identifier, replacing ",," with ",0," and then resending the corrected stream at 19200 baud, and handling the sentence postfix. Resending at 19200 baud should be able to fit between the sentences if your GPS is sending $GPGGA, $GPRMC, $GPGSA and $GPGSV at 1Hz. Certainly not the most elegant strategy, but seems to be a small-space solution to the above mentioned issues, within the small part of my world I control.
Peter's postfix is certainly the fastest and most code-efficient approach, but has a couple of issues off the shelf. I'm certainly looking for the fastest route from what the GPS actually sends to what he initially proposed. I'm not trying to criticize, but want to keep jay-t (a newbie like me) from pulling his hair out over a couple of critical and already identified issues. I thought I was doing good with a three-page method to accomplish what Peter came so close to accomplishing in ten lines.
Since the KISS just uses the comma as it would a space then it's just empty space to Forth but there's an easy fix for the empty CSV value problem and as always it's only a couple of lines of code if that. This little routine should work as it remembers the last character and checks for a double comma in which case it returns with a "0" instead and forces the second comma back into the stream with a KEY!.
Why is the last print giving a "0" in aa, and giving a "1" in ab?
I would expect the stack to be empty.
These two definitions can be reduced to simple one liners like this:
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR
The trouble is that at the end of the second test you are dropping the remainder of $CC which is now 0 off the stack and telling Forth to print a number from an "empty" stack except there is always a "top of stack". If I paste in these one liners this is what I get.
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR CC
00110011
0
ok
ok
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR CC
00110011
1
ok
Now before I run it I pad the stack with two numbers 12 34 then do it again I get:
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT CR . CR CC
00110011
0
ok
ok
$CC DUP . CR 8 FOR DUP 1 AND . 2/ NEXT DROP CR . CR CC
00110011
34
ok
So you can see there is nothing mysterious it's just you are operating outside of defined parameters and I forego a lot of this "stop everything and report error code mumbo-jumo" stuff which while fine on the testing bench only disrupts a system which could otherwise continue operating normally without downtime.
TIP: There is never a need to prefix a # to single digits 0 to 9 as these will always be decimal but as a tip you could leave your system in DECIMAL mode and only prefix the hex and binary numbers. Also 2/ is more efficient than 1 SHR.
Also there is no difference in TF interactive mode and compile mode, it always compiles, so you can use your loops and conditionals without having to create a definition or use "special" forms of those words as is common in traditional Forths.
Thanks for the 12 34, now I can test my pub's.
When I use . on the terminal with nothing on the stack I get -1 back, I understand this. But when I use . in my pub same condition, I get 1 back, this confused me a bit.
With 12 34 I am back on track.
But the next little pub confuses me a bit.
All in hex, 15 and 17 I understand, but 16 I do not.
Am I too sluggish on the uptake.
15 . 15 ok
16 . 10 ok
17 . 17 ok
16 is one of the fast constants like -1, 0,1 ... 8,16, BL for 32, FALSE = 0, OFF = 0, ON = -1, TRUE = -1
it is intercepted and handled independently of BASE setting.
so definitely 16 independently of the base set is #16 $10
so better set default base to 10 and mark all hex numbers clearly with $.
maybe it should be renamed to #16 - can still be a fast constant.
Maybe you better check all sequences up to 4294967296? Do you want to be a beta tester?
16 is one of the predefined fast constants just like 0 1 2 etc but unlike 0 to 9 which are the same in hex or decimal there is this little problem that you have come across. But I had decided by then when I had added it to the kernel that my hex numbers were all going to be prefixed to avoid any confusion at all. So if I meant 16 in hex then I would say $16 and indeed it's a force of habit now that all hex numbers I type in are prefixed.
Now I have an additional leaning towards leaving the system in decimal mode as I can easily control radix of input and output without affecting everything globally. The variable base is a very nice feature of Forth but it also creates problems too which is why I have ways to force recognition of numbers. So my source code will always have hex numbers prefixed with $ but I have used # to make sure my decimal numbers are decimal but maybe if I leave the default base set to decimal then these won't need to be prefixed and if you see 16 just as it is, then it really is 16 decimal.
As for printing numbers there are plenty of support words for that including the very flexible .NUM word which accepts a control word to specify digits, base, separators, leading blanks or zeros etc.
Actually that brings up another point, the traditional dot that's used in Forth to print a number is a bit too terse in source code form as a tiny dot gets lost whereas during debug it's quick and easy. That's why I have aliases for many of these words such as PRINT and PRINT" so that these words stand out clearly in source code rather than . and ." so perhaps .NUM needs an alias too, PRINTNUM.
The proposed solution:
Move the dictionary out of RAM into a file in such a way that words can be found quickly and easily, preferably at normal source code compile speeds.
The result:
The dictionary optimization code is up and running and fully integrated into Tachyon now. It sorts and stores the dictionary into indexed sectors so that for any word there is an indexed sector where the valid word will be found along with it's attribute and code header. The 7-bit index is generated from 5 lsbs of the first character plus 2 lsbs of it's count and this 7-bit index is used to open a 512-byte sector within the 64KB WORDS.DCT file.
This method is fast and even if it needed to read in a sector for every word it only takes around 3ms per word. However a cache is implemented which pushes the latest header into the start of the cache including any matches in the cache itself to keep it fresh. So before a sector is indexed and read in, the cache is searched for a match first, which after it starts to fill up will end up scoring more and more hits, especially since it keeps it's hits fresh. The oldest words that get pushed or partially pushed off (variable lengths) the end of the cache become invalid anyway due to a null termination area.
This works but that still leaves the problem of adding new words to the dictionary easily. Since the normal Tachyon dictionary grows downwards in memory it becomes a simple matter to say that the start of the cache is the default start of the dictionary. So instead of searching the "cache" Tachyon will use it's normal method of searching from "names" (pointer to the dictionary) and initally it's the same thing. Now as new defintions are created they are prepended to the "dictionary" which is now a combination of the new words growing downwards in memory but still cascading into the cache. It just works, and really well at that.
To test it out properly I loaded my datalogging application onto a full system with EASYFILE and EASYNET running. Normally even after reclaiming all the private headers (about >2KBs worth) there is no room left after this and no easy way to add more to it. Now I leave all the private headers intact, have the application fully loaded in and there is around 6K free.
You would also think that looking up each word from SD might be slow but the combination of indexed sectors, cache, and dictionary let me load my application in without any extra delays.
This paves the way for adding even more features to what is essentially a little 32KB microcontroller. It's now possible to include VGA buffers and many other things including the assembler etc.
<link to SDWORDS.fth>
Out of interest here's a MAP listing with comments which prints out a packed byte for every xN bytes, some "bytes" are comprised of 16 or 256 bytes etc. However the main area of interest is in red where even with the whole dictionary intact and networking and application we still have almost 6K free. In terms of Forth code that's a whole continent.
.MAP
REGISTERS x16 (Forth registers)
0024: .. .. .. .. 02 .. .. .. .. .. 01 .. 04 05 01 ..
VECTORS x64 (all high level defs have a 16-bit vector)
0124: 11 11 14 11 12 0D 18 0E 14 13 14 13 15 17 14 13
0524: 14 15 14 15 13 12 15 18 19 18 18 18 18 16 1A 0D
KERNEL CODE x256 (high level bytecode defintiions)
0928: 63 63 88 88 6A 64 5B 68 71 64 79 64 58 73 77 42
CODE x256 (EXTEND.fth + UTILITES + EASYFILE + EASYNET + APPLICATION)
18C6: 64 5C 54 5B 72 6B 6D 64 66 67 6C 84 51 6A 60 6D
28C6: 5D 6B 80 49 23 6A 70 6E 35 4B 3E 65 43 64 5B 5E
38C6: 75 72 72 55 68 96 73 60 78 73 6D 61 88 7C 84 7B
48C6: 67 85 6D 74 65 80 74 7C 6E 77 58 69 81 66 41 ..
SPARE @576B: for 5,779
DICTIONARY x256 (new words added after converting dictionary to file)
6DFE: 6A
(cache and private dictionary sector sit in here = 1K total)
BUFFERS x64 (There are four 512-byte sector buffers for four open files in EASYFILE)
7500: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
7900: .. .. .. .. .. .. .. .. 11 15 19 1D 21 25 29 2D
RX BUFFER x64 (Serial receive buffer - laid down over the PASM image)
7D00: 0E 0C 0C 0C 0E 0D 0F 0E
HUB STACK x16
7F60: .. 01 01 .. .. .. .. .. .. ..
Also at the bottom of SDWORDS.fth you talk about loading all the goodies except the app and running SDWORDS then initializing with !SDWORDS.
So step one: create a 64 or 128K blank file named WORDS.DCT on the SDCard
Two: load up tachyon the way you want your kernel upto the application you are building/working on.
Three: Run SDWORDS from the SDWORDS.fth module
Four: BACKUP
Five: initialize your rebooted Tachyon sessions with !SDWORDS to use the dictionary interactively and/or compile your app
Six: you are able to simply forget your app or reboot for development ease
Seven: backup again after your app is ready
Eight: now you have so much code space you are bound to have even more fun
Is this about right?
I will probably try to automate this as easily as possible but for the moment I am invoking operations manually to check it all. Right now I'm seeing if I can load up the full EXTEND.fth etc, compact the dictionary after EASYFILE, load EASYNET, and compact again. In fact I may just create the word COMPACT to do this automatically so it works out whether it's a new dictionary or an addition etc.
So steps at present are:
1. Load in SDWORDS.fth on a system that has EASYFILE already loaded.
2. Ensure you have a file called WORDS.DCT with at least 64KB available (only uses 64KB)
3. Run SDWORDS which will populate WORDS.DCT sectors with those words as they are indexed
4. When you want to release the old dictionary in RAM then run !SDWORDS which will init the cache, reset the dictionary to the start of cache, and revector the SEARCH method.
5. BACKUP after !SDWORDS if you want to lock all this in now. Run .STATS to see how much RAM is now available.
6. Load app normally and this can also be forgotten normally as well. Only way to forget compacted stuff at present is to do a COLD and start again.
7. Forth Freaking Frolicking Fun
Newer method may just use the one word COMPACT and then manually BACKUP in case there is an ooops!.
BTW, I still have to add an FCREATE to EASYFILE to allow new files to be created in the system although I had been trying to avoid any FAT32 housekeeping on the embedded side previously.
Just created COMPACT and tested it all out which is all you ever need to worry about for now and you can even compact the dictionary as you go as it will detect whether it is a new or additional operation.
To test this out I loaded a full EXTEND.fth so it included the whole kit and kaboodle, loaded EPRINT, my hardware headers, SDCARD, EASYFILE and without trying to reclaim any headers as there is no need to I then loaded SDWORDS and had 1.4K free.
Ran COMPACT and ended up with 9.3K free.
Added W5200 and EASYNET and ended up with 1.7K
Ran COMPACT and finally still have 3.9K free even with all the EXTEND fluff and ALL the headers intact!
LISTWORDS Mounted SD Card
Media mounted as 71CC.0203 mkfs.fat WIDGET FAT32 Cluster size = 4,096 Sectors = 4,512
0000: @gateway @sip @mac @PAD @CNT @SCK @SCL @rtc @FAT @ATR @CMS @RCD
0001: !PCB !WIZ autosend ANDN AVAR ansi ATN? ATN@ ATN! !ADC ADC@ APIN
0001: !PCB ACMD !SDWORDS
0002: BYTE base BELL BYTE BITS BOLD BPIN boot
0003: CON] CASE contimer #CONNECT CDUP cwd$ CASE COG! COG@ CALL C@++ CONSTANT
0003: CNT@ CON] CTRA CTRB COMPARE$ #adc CTR@ CTR! COLD CLUSTER@
0004: dataport DROP DIRA DIRB DUMP DUTY DAC! date days DAY@ DAY! dir$
0004: DIR? DIR! dictsect dict
0005: ELSE eNET EMIT ELSE EXIT ECHO ESC? ESC[ ENQ! ENQ@ EERD ERRORLED
0005: EASYFILE.fth
0006: FILE &TIMEOUT &CON FlushSkt FEAT FREE FILL FRQA FRQB FIND &TR1 &TE1
0006: &TR2 &TE2 FILE fats file FREM fkey FGET FPUT FCREATE$ FRUN
0007: GETFNAME GET$ GRAB
0008: HTTP HEAD (CREATE) HERE here HOLD (.") (OPCODE) HIGH HOME (VA) (CLKSET)
0008: HELP (FORGET) HEX: (ls)
0009: ifconfig IDLE I2CSTART I2C! I2C@
000A: JUMP
000B: KOLD KEY@ KEY! KEEP +HEX +NFA +SDWORDS
000C: LEDS LOOP LREADSKT LIST LEN$ LOOP LONG LONGFILL LIKE leds LEDS
000D: myGW myIP mySN MDTM MYOP MASK mod2load MID$ matchstr MUTE
000E: .IPX .IP1 .@SKTBUF .PTR .NET .SOCKETS nettimer .BYTEDEC .IPD NFA' NOTFOUND .DEC
000E: .VER .HEX NEXT NOOP .NUM .MODULES .PIN .MAP .LAP .AUTORUN .DAY .ASMONTH
000E: .FAT .ATR .DIR NFA>SECT
000F: oui2 OVER OUTA OUTB
0010: PASS PORT PASV PRIVATE] PIN! PIN@ PHSA PHSB PINLOAD? PWM% PWM! PCB$
0011: QUIT
0012: READYLED RNFR RNTO RETR REST 2DUP REVECTOR RS232LED RS485LED READYLED RES@ rsvd
0012: root
0013: SET? SKT@ sDIP sPRO sCONNECT sSENDMAC sCLOSED? sDISCON? skt$ SYST SENDFILE SIZE
0013: STOR SET? STOP SWITCH>< SWAP SPR! SQRT SARX SDERRLED SDBSYLED sdwr sdrd
0013: sdhc SD2@ SD4@ SDRD SDWR sect/fat SPLITDEC SDSEARCH
0014: TRUE THEN txwr type TYPE TERMINAL TASK THEN TRUE TYPE TASKREGS time
0014: TIMEOUT? timerstk timerjob TIMERJOB tval term
0015: USER ukey unum UNSMUDGE udir
0016: VCFG VSCL VCLS
0017: WAITSEND word WKEY WORD WATCHDOG WRITEHEX
0018: XSHR XADR
001B: [CON [WS2812] [PWM32!] [PRIVATE [CON [C,] [W,] [EPRINT]
001C: <CR>
001D: =dtk
001E: >PFA >VEC >F83
001F: ?FTP ?CONTENT ?LED ?EASYNET ?DUP
0020: @BASE @txwr @ @HATR @MOSI @MISO @FILE @ROOT @BOOT @FEXT
0021: !TXWR AUTO! ALLOCATED ALLOT AGAIN ! ALIAS ALIGN ANSI? A ALARM !LEDS
0021: !SDIO
0022: BREAK BREAK BCOMP " BEGIN BYTES BDUMP blksz B BCD++ byte/sect
0023: CONIO contd COMMA codes # COGID CMOVE CLOCK CONIO CTYPE COPY$ CALL$
0023: CountDown card? CARD? cache
0024: DEBUG DEPTH DISCONNECTED? delim DEBUG DEPTH DELTA DUMPL DUMPW DUMPA DATE@ DATE!
0024: DATE@ DATE! dboot
0025: ERROR ERASE ENDIF EMITS % ERSCN eeadr ENDRD ESAVE ELOAD eebuf ECOPY
0025: EFILL eFILE
0026: &RECV fsave flags FALSE &WNDO &WNCS &WNCK &WNDI &IOCS &SFCS &SDCS &SDDO
0026: &SDCK &SDDI FILE# &sdto FLUSH fat32 fboot file$ FILE$ FILE@ fread fstat
0026: FPUTB FILE> FOPEN FLOAD
0027: ' getsz '
0028: ( HDUMP (KEY) ( (WAITPNE) (WAITPEQ) (NULLOUT) (rnd) HYPOT (SEP) (BDW) (ESC)
0028: (COGINIT) (RECLAIM) (MAP) hexch (cat) (DIR)
0029: I IPRAW INTS@ IFDEF I IMMEDIATE I2C!? IDUMP INFO@
002A: *end* * J
002B: KEEPALIVE +CALL +LOOP + K +CHAR +DIR!
002C: LREAD LSEND LCKEY ledon lines , LEMIT LONGS LEAVE LBIT! LOCAL
002C: LEFT$ lpace lscnt LISTWORDS
002D: MOUNT - modloaded MASKS MASK? MATCH mount MOUNT
002E: names . .LONG .WORD .BYTE navar .HEAD .DECX .ADDR NULL$ NORMALIZE .ATRS
002E: .NAME .SPRS .PINS .HHMM .TIME .DATE .TIME .DATE .CARD .LIST .FILE
002F: /
0030: 0 PLAIN PPPoE PORT! pass$ 0EXIT 0 Published PRINT PINS@ PLAIN PWM.START
0030: PROCESS_TOKEN parts
0031: 1 QUIET 1
0032: RETRYTIME RXSIZ RNFR$ RESET 2 2DROP radix RADIX RI232 RDRTC READFAT32 RCDSZ
0032: ROMSZ
0033: 3 SHL16 sMODE sCMD! sINTS sSTAT sPORT sDHAR sOPEN sSEND sSENDKEEP sRECV
0033: sCLOSING? sendtimer SCRUB SPACE SPIRD SPIWR 3DROP 3 SWAPB STRIP SERIN SI2C!
0033: SI2C@ SDBUF SDCLK STAT@
0034: 4 TXSIZ tasks 4 TIMES TABLE TASK? TIMER ttint TIME@ TIME! TIMERTASK
0034: TIME@ TIME!
0035: UPORT user$ ufind uemit UNTIL U/MOD UNPRIVATE UPPER uboot ucard
0036: vread
0037: W5200.fth wMODE WCOLD WHILE WORDS WRRTC
0038: 8 8 XSAVE XLOAD XADR!
003A: :
003B: [COMPILE] ; { [COMPILE] [SSD] { ;
003C: < \ < | \
003D: = } =
003E: > ~ >CHAR > ~ ^ >CSTR >FILE
003F: ?EXIT ?SENDPOLL ?SEND ?HTTP ?EXIT ?DICT
0040: @wcold @ports @NAMES @X @. @WRFLG @FNAME @CTIME @CDATE @ADATE @FTIME @FDATE
0040: @FCLST @FSIZE @FREAD
0041: !WIZIO !WIZIP ADCBUF APPEND.BLK APPEND
0042: BUFSIZ BINARY BL BOUNDS BACKUP BLKSIZ blklen
0043: C@ C! CR CLOSED CONNECTED? CREATE CREATEWORD create #S #> CR C,
0043: COGREG CMPSTR C! C@ C~ consav CURSOR CLKSET CE1372.fth CTS232 CLUST>SECT
0044: DO disreq DISCONNECT digits DO DOUBLE DS dwidth $! $= DTR232 DSR232
0044: DCD232 diradr dirbuf
0045: eW5200 errors EXTEND.fth %% ERLINE ERASE$ EEPROM E@ E! ESAVEB EE EPRINT.fth
0045: eprint EPRINT extbuf
0046: FTPDAT FTPMSG FORGET FIXBIN fatptr fname$ FSIZE@ fwrite FINPUT FCLOSE FOPEN# FOPEN$
0046: FDATE! FTIME! FSTAMP
0047: GO '' GETRND
0048: (RETR) (STOR) (EMIT) (.NUM) HZ hexbuf hexptr hexadr hexflg (SDWR) (SDRD) (.DIR)
0049: IF IFNDEF IF INPUTS INVERT IN IX indent
004B: +! ++ +TIMER +CACHE
004C: LANLED L! L@ LWRITE LANKEY LCEMIT LANCON ledcnt LANSKT LOOKUP LSTACK L>
004C: locals LANLED LOGSZ? ls
004D: ms MACRAW ms -1 -- m@ M@ MATCH? MY ma
004E: NETMAN NUMBER .S ." NEGATE .INDEX .TASKS .BLOCK .STATS .BOOTS .DTFMT .FYEAR
004E: .FDATE .FTIME .FNAME .UTIME .FILES
004F: ON OUTCLR OUTSET OR ON OK ok // /ROUND OPCODE
0050: PRINT" prompt PRINT$ 0< 0= PUBLIC PRINT" P@ P! PINSET PINCLR PININP
0050: PLLDIV pwmtbl PULSEWIDTH PRINT"
0051: 1+ QW 1- 1+ 16 QHYPOT QW QWORDS QV
0052: REBOOT RXREAD RETRYS REBOOT REPEAT 2- 2+ RUNMOD R> 2* 2/ RETURN
0052: RADIX> RCFAST RCSLOW RIGHT$ RTS232 RXD232 rtcbuf RW RENAME
0053: SPIWRW socket SOCKET sDPORT sSSIZE sRXMEM sTXMEM SUBNET sCLOSE sINACTIVE? sktbuf switch
0053: SEARCH STACKS SWITCH SPIRDX SPIWRX SPIWRB SHRINP SHROUT STREND SETMOD SPACES STRING
0053: second SEROUT SI2C!? SETPWM SDCARD.fth sdinfo sdpins sdsize sdbusy sector scanch SDBUSY
0053: SDPINS SDERR? SDDAT! SECTOR SD sect/clust serial
0054: TXREAD txtime txsize TELNET TO timers TXD232
0055: un UpdateTXWR U. us U< U/ UNLOOP U> U! U@
0056: vwrite VC VP VOLTS@ VirtualRTC
0057: W! WBUFSZ WITHIN W! W@ WORDS: W~ WWORDS WRESET WPWRDN wrflgs WRSECT
0058: XCALLS X1 X2 X3 X4 XY X@ X!
005B: [NFA'] [WS2812CL] [PLOT] [SDWR] [SDRD] [SPIO] [~
005C: <CMOVE <# || <> << |< <=
005D: ]~ ]~ == =>
005E: >UPPER >DIGIT >L >R >B >N >RADIX >W ~~ >> >|
005F: ?CTRLS ?SDTIMEOUT ?MOUNT ?WRDCT
0060: @subnet @SOCKET @TXBASE @SKTBUF @CE @SPISCK @EE @EEWAIT @FCLSTH @DIRBUF @FWRITE
0061: AND !SP !TXBUFS aUTORUN autorun !SP !RP ABS AND ADO APPEND$ any
0061: ANY atr ackI2C@ AUTORUN !SD ACTIVE?
0062: BYE blkpoll BLKSEND BUFFERS baudcnt BCA BRANCH> B>W B>L bdw BCD>DEC
0063: C+! CON C~~ COMPACT #SOCKET CONNECT constat #ftpmin #ftpmax CWD CONTENT CONSOLE
0063: COGINIT CLS COGDUMP CLR C+! CLKFREQ CREATE$ CON COGREG! COGREG@ C++ C--
0063: C~~ CNT COS COGINIT CLK ctr CTR CTRMODE COUNTUP CONBAUD #EP cid
0063: csd crc CMD clshift cat COMPACT
0064: DISCARD DISCREQ DISCARD DECIMAL DUP DS+ DPL dst DUMPROM DEC>BCD DIR dictflg
0065: END EASYNET.fth ECHOREQ EASYNET EXECUTE EC! EC@ EW! EW@ EVERIFY END EXTEND.boot
0065: eSD eof
0066: filesel &SENDOK &DISCON FTP FINDSTR FOR FRQ filesel fatname FC! FOUTPUT FRESHEN
0067: GATEWAY GETPAGE GET GETWORD GETLINE
0068: (LSEND) HEX (:) (WORDS) (STRIP) HEXLOAD (SLIST) (.LIST)
0069: INTMASK INA INB I2CPINS I2CSTOP
006B: keypoll keypoll KEY KHZ
006C: LC! LW! LC@ LW@ LANSEND LANEMIT LAN LITERAL lastkey LOADMOD LAP LOW
006C: L>W LSP LOCATE$ LISTKEY LED LEDREG! LOADROM LOADIMG
006D: --- MAC -NEGATE --- MODULE: MIN MAX MOD mc@ mw@ MC@ MW@
006D: MODPINS MHZ MARKER? -FERASE
006E: .IP .SOCKET netflgs NFA>CFA .STACKS NOP NOT NIP ... NULLOUT .RETSTK .I2CBUS
006E: NCO .TIMERS .DT .SW .DT .SD nfasect
006F: OFF OUTPUTS OFF OUT org ORG ocr oemname
0070: pri PRIVATE pub PWD pri pub 0<> PRIVATE PFA>NFA PAR PLL pwmfreq
0070: pwmmask PWMCOG.TASK PULSEWIDTHS PCB
0072: RXSIZE@ RXWRITE RXBASE0 RXMASK0 RESTART READBUF REG REV ROR ROL ROT RELEASE
0072: RND RAM REVERSE RUN runtime RECLAIM rootdir RENAME$
0073: seconds SKT SIP sLISTEN sDISCON sESTAB? sCONNECTED? SetPORT SPINNER SWITCH= SWITCH@ SP!
0073: SPIWR16 3RD SET SHL SHR SPR SIN SQR SPIPINS SETPINS SAR seconds
0073: SERBAUD SCL SDA SETPWMS SETPWMW sectcrc SD@ SD! scancnt scanpos sdtimer SDRDBLK
0073: sectors SAVEROM SAVEIMG SDWORDS.fth SDWORDS
0074: TXFREE@ TXWRITE TCP TXBASE0 TXMASK0 TACHYON 4TH TAB tid TIMEOUT
0075: UDP UIP UNKNOWN UM* U.N
0076: VER volname
0077: wizpins WIZ WAITCNT W+! WAITPNE WAITPEQ W>B W>L W++ W-- W~~ WAITKEY
0077: wdt
0078: XOR XC@ XC! XW@
007B: [PWM32] [SPIOD] [ESPIO]
007C: \\\
007E: >|RCDSZ
007F: ?SDCARD ?BACKUP ?TELNET ?DISCONNECT ?NEGATE ?BACKUP ?SDCARD ok
.STATS
Propeller .:.:--TACHYON--:.:. Forth V24141103.0500
Clock frequency = 80,000,000
NAMES: $70E0...74CD for 1005 (-1364 bytes added)
CODE: $0000...617C for 23951 (3091 bytes added)
CALLS: 0000 vectors free
RAM: 3940 bytes free
Whoa, 4K free, that is a giant tachyon / forth app since the kitchen sink has been loaded already.
if I remember correctly there are 1k call vectors in the XCALL, YCALL, ZCALL, WCALL tables each 256 entries.
Aren't we at the limit now ?
Yes, you noticed, but it's all being taken care of. One of the main reasons I used vectors in the first place was so that Spin tool compiled bytecode kernel would play nice with bytes but not words, otherwise every single word would have to be broken up into high and low bytes with offsets etc. Doable, but such a pain. Secondly, using vectors meant that we could reduce a high-level call from 3 bytes (opcode+address) to 2 bytes where one of four opcodes each picks one of 256 16-bit vectors from the table.
So there is nothing to stop us from using 3 byte calls at a higher level on top of the kernel. I have implemented the CALL16 and getting back to modifying the BCOMPILE word so that it if it runs out of vectors for new words it will just compile a CALL16 opcode followed by the absolute address of the function it is calling. No big deal and at this level there is no real difference in code size as it is only the references to new unvectored words that take an extra byte.
Sometimes I feel like Harper from Andromeda, yeah baby, well at least when it all comes together and it works. When it doesn't then sometimes I feel like Harper from Andromeda.......IYKWIM
Works fabulously! In efforts to be constantly wary to save vectors and bytes, I found an issue I don't understand...Can you see my error?
works perfectly, while does not.
Your mini tutorial was helpful, but one added question: Is there a way to keep track of what part of the buffers has been claimed? Since my data is started at BUFFERS, I need to increment the "buffer" area to keep from wiping out the data I want preserved...OR is there a better way to ORG so that I don't run into the same issue where data is being stored in code space?
It looks like you are over-thinking things a little as ORG and DS etc look after that stuff for you. The thing is that you can use this in one part of the code and then use it later and it still points to the next available area. But if you really want to know where it is just look in the org variable (lower-case). But you don't need to keep setting ORG, normally just once is fine.
The 10 us is redundant here, it was used at the end of a transmission to ensure a minimum gap period, that's all.
Now, it normally helps if I can see all your code rather than snippets but I'm wondering what you are trying to do by nesting a definition within a definition? Perhaps you copy and pasted without removing the ; after the FILL ?
Doesn't nesting like this save memory? Obviously not a lot in this case, but I'm trying to be as efficient as possible from the start so as I learn and as my projects become more complex, I've developed good habits..Nesting is, of course only after I've tweaked and verified, when I'm cleaning up and commenting my code for later interactions.
After I implemented your ORG recommendation ... If I tried it blanked out the data I was trying to preserve...I have packaged the whole thing into a module and have BACKUP'd the Prop. BUFFERS . returns the address of my ORG'd data ( wsgrn . = 7500, BUFFERS . = 7500)
...Is BUFFERS ORG the wrong way to setup the variables?
I'm also not seeing the advantage of ERASE then FILL. Is there one I don't see other than cleaning up pseudo-random data for possible later use?
The semicolon just after the fill compiles as an EXIT so the code that you have after that will not execute plus this is something you never do in any Forth although Tachyon does allow multiple entry points.
Also remember that $00 compiles as a 2 byte opcode [BYTE] [00] whereas 0 is an opcode and compiles as a fast single byte opcode [0]. In this case it is neither here nor there but be aware of it if.
Once again I can't help you too much when I can only look through a keyhole, just post all the code. For instance you have wsbytes specifying the number of bytes for wsdata but I don't know what value that is. Then you have pxlbytes which is another value I don't know. However BUFFERS $800 ERASE is going to wipe everything for sure, is that what you want?
Try putting things like BUFFERS $800 ERASE on one line by itself and give it a comment so that you at least will know what you are trying to do, then put that BUFFERS pxlbytes + pxlbytes $FF FILL on it's own line with a comment. Be methodical and try talking to yourself as if you are explaining what you are doing to someone who is trying to follow along. As you verbalize your thoughts you will do as many of us have done and go "you idiot" or "of course, why didn't I see that before" etc.
BTW, ERASE is the same thing as 0 FILL.
Tachyon forum html.zip
tachyon forum.txt.zip
The bot should search light with it's phototransistors. I did a run with motor driver battery unplugged:
Propeller .:.:--TACHYON--:.:. Forth V24140928.1630
Clock frequency = 80,000,000
NAMES: $5B01...745E for 6493 (102 bytes added)
CODE: $0000...3DBF for 9314 (577 bytes added)
CALLS: 0420 vectors free
RAM: 7490 bytes free
jimmy light measure
turn right
driveahead
driveahead
driveahead
...
It just repeats the "driveahead" message over and over again. Without jumping back to the BEGIN statement.
Here is the program:
It should print out the "light measure" message after "driveahead" by jumping back to "BEGIN", but it never does this.
Maybe I messed up something with my IF/ELSE/THEN stuff?
I just have no idea what is going on there.
Then I developed Tachyon and one of the early snippets of code I wrote was for a bufferless wave player that read continuously from the SD card even though I hadn't written any filesystem stuff yet. Over the the weekend I just played with playing back files in a few different ways and I found that I could just manage 44,100Hz sample rate playback totally in Tachyon. Anyway I've tidied it up a bit and actually put in a small RUNMOD so I can push it harder and which grabs one sample, scales it, add bias, and feeds it out. So except for those dozen lines of PASM in the kernel, the rest is totally Forth.
This code here will play a file in the background, and you can PAUSE or CONT or FADE or vary pitch interactively. Type PLAY <thisfile> to start play or add a small macro like TRACK so you can say 123 TRACK and it will play TRACK123.WAV. The full kit may come later but this is fun to play with. BTW, I use Audacity and convert MP3 stereo to mono, normalise, and export as a MS WAVE file.
Code size = 735 bytes
Names size = 335 bytes
Just un-possible, I like this form of the word, now to get something warm to drink and unpack these words. Heater's gonna go crazy, I can hear it now (WHAT ABOUT FFT)
I was looking after a 1-wire object, but couldn't find one.
I saw something in the dropbox, but it was a mix of spin and forth.
So I started my own development of a 1-wire in forth.
Now I have it running and spitting 3 temperaturer back on me.
I have had a lot of fun, and it is almost like assembler.
Have a look at the object.
It seeks the 1-wire and returns the id numbers of the chips with crc8 check.
Then it send the temperature back on the console.