UPDATED! January 12, 2019 (pubdoc link) (edit/comment version)
Back in 2012 I decided to write Tachyon Forth for the Parallax Propeller P8X32A or as we refer to it now simply as P1. Briefly, the reason for writing a Forth was not for Forth's sake although the appeal there was an interactive development environment resident on the P1, it was to get more out of the limited memory of the P1, but to also do so as fast as was possible for what is essentially a virtual machine with the view that serious commercial products could be produced based on the flexible P1.
In the years that followed Tachyon has proved itself, through the various original bytecode versions through to the version that now uses 16-bit word codes. The successor to the P1 is simply referred to as the P2 and although it is only available at present as an FPGA image for testing and development, nonetheless serious testing has been carried out for many years. As part of this testing I ported Tachyon across for the P2 and now Chip has announced that the P2 to be available hopefully sometime in 2018 will have 16kB of ROM of which he would only be using a couple of kB or so for the boot loader etc.
TAQOZ is implemented primarily for debugging hardware or testing out "what ifs" but it is still nonetheless a full Forth custom designed for the P2 and its hardware. As it stands the native TAQOZ wordcode can address code in a 16-bit address space while dictionary and data may reside anywhere in memory. So having software written in TWC (TAQOZ Word Code) is very compact and fast and 60kB of code represents a very very large program whereas FAT32, Ethernet servers and VGA including the Tachyon kernel and extensions would all fit in around 20kB of code space on the P1.
TAQOZ provides an interactive hardware debugging environment through its Forth console via a serial terminal emulator. Even without any "software" it is possible to exercise the hardware and write code on top of the TAQOZ dictionary, thus extending the language's vocabulary of words, all of which are a combination of various functions, constants, variables etc that are referred to by name. Parameters though are passed postfix style so that data is pushed onto a data stack and precede functions.
Connect a terminal and I recommend using ones that don't drop characters and support ANSI such as TeraTerm, RealTerm, GtkTerm, Minicom. You can use any baud rate from 9600 to 2M baud (or more) but by default select 115200, 8 data, 1 stop, no parity, no handshakes, no echo, no extra CR/LFs etc.
Now that you have your P2 connected to a terminal you can type a "greater than sign" and a "space" to autobaud the serial loader after which you can hit "escape" to escape to TAQOZ. That is the ASCII key sequence for $3E,$20,$1B.
If all is well you should have the splash logo and the prompt.
Cold start ------------------------------------------------------------------------------- Parallax P2 .:.:--TAQOZ--:.:. V1.1--v33h 190219-1900 ------------------------------------------------------------------------------- TAQOZ# |
Now enter WORDS to see which words are in the dictionary (or hit ^W)
TAQOZ# WORDS --- DUP OVER SWAP ROT -ROT DROP 3RD 4TH 2DROP 3DROP NIP 2SWAP 2DUP ?DUP AND ANDN OR XOR ROL ROR >> << SAR 2/ 2* 4/ 4* 8<< 16>> 8>> 9<< 9>> REV |< >| >N >B >9 BITS NOT = <> 0= 0<> 0< < U< > U> <= => WITHIN DUPC@ C@ W@ @ C+! C! C@++ W+! W! +! ! BIT! SET CLR SET? 1+ 1- 2+ 2- 4+ + - UM* * W* / U/ U// // */ UM// C++ C-- W++ W-- ++ -- RND GETRND SQRT SETDACS ~ ~~ W~ W~~ C~ C~~ L>S >W L>W W>B W>L B>W B>L MINS MAXS MIN MAX ABS -NEGATE ?NEGATE NEGATE ON TRUE -1 FALSE OFF GOTO IF ELSE THEN BEGIN UNTIL AGAIN WHILE REPEAT SWITCH CASE@ CASE= CASE> BREAK CASE ADO DO LOOP +LOOP FOR NEXT ?NEXT I J LEAVE IC@ I+ BOUNDS H L T F R HIGH LOW FLOAT PIN@ WRPIN WXPIN WYPIN RDPIN RQPIN AKPIN WAITPIN WRACK PIN @PIN ns PW PULSE PULSES HILO DUTY NCO HZ KHZ MHZ MUTE BLINK PWM SAW BIT BAUD TXD RXD TXDAT WAITX WAITCNT REBOOT RESET 0EXIT EXIT NOP CALL JUMP >R R> >L L> !SP DEPTH COG@ COG! LUT@ LUT! COGID COGINIT COGSTOP NEWCOG COGATN POLLATN SETEDG POLLEDG KEY WKEY KEY! CON NONE COM CONKEY CONEMIT SEROUT EMIT EMITS CRLF CR CLS SPACE SPACES RAM DUMP: DUMP DUMPW DUMPL DUMPA DUMPAW QD QW DEBUG lsio COG LUT KB MB M . PRINT .AS .AS" .DECL .DEC4 HOLD #> <# # #S <D> U. .DEC .BIN .H .B .BYTE .W .WORD .L .LONG .ADDR PRINT$ LEN$ " ." CTYPE ?EXIT DATA? ERASE FILL CMOVE <CMOVE s ms us CNT@ LAP LAP@ .LAP .ms HEX DEC BIN .S WORDS @WORDS GET$ SEARCH $># @DATA HERE @HERE @CODES uemit ukey char delim names TASK REG @WORD SPIN | || , [W] ["] NULL$ $! $= ASM FORGET CREATE$ CREATE VAR pub pri pre : ; [ ] ' := ==! ALIGN DATCON ALLOT org bytes words longs byte word long res [C] GRAB NFA' CPA CFA \ --- ( { } IFNDEF IFDEF TAQOZ TERM AUTO SPIRD SPIRDL SPIWB SPICE SPIWC SPIWW SPIWM SPIWL SPIPINS SPIRX SPITXE SPITX WAIT CLKDIV RCSLOW HUBSET WP WE CLKHZ ERROR SFPINS SF? SFWE SFINS SFWD SFSID SFJID SFER4 SFER32 SFER64 SFERASE SFWRPG BACKUP RESTORE SFRDS SFWRS SFC@ SFW@ SF@ SF .SF SDBUF sdpins MOUNT DIR !SD !SX SD? CMD ACMD cid SDWR SDRDS SDWRS FLUSH FOPEN FLOAD FGET FREAD FWRITE SECTOR SDRD SDRDS SDADR SD@ SD! SDC@ SDC! SDW@ SD @FAT @BOOT @ROOT fat END 432 ok TAQOZ# |
These are all the words that TAQOZ understands that you can type in and run or build into new words that are added to the dictionary. Let's just try interacting with TAQOZ for a moment and make an LED blink. Assume we have an LED connected to P5 we can make it blink very simply with 5 BLINK which is defined simply as PIN 2 HZ where the word PIN takes the argument 5 off the top of the data stack and HZ takes the 2 and configures the selected pin as an NCO with a frequency of 2 HZ. Since TAQOZ boots up in RCFAST it is running at a 25MHz speed rather than the 80MHz that was was used in the FPGA version, so it will be slower. To make it blink faster and since the pin has already been selected through PIN we can just type in 10 HZ instead or any other value.
Remember that all words and numbers should be separated by at least one space but they can even be on separate lines.
TAQOZ# 5 BLINK ok TAQOZ# 10 HZ ok |
The HZ word is using the smartpin mode so it doesn't need any code running continually to make it blink but to make the smartpin stop type MUTE
If we have multiple LEDs connected to P0 to P7 we can make them all blink in one simple loop 8 0 DO I BLINK LOOP which specifies a limit and a starting index used by DO to setup a loop consisting of I BLINK . The I leaves the current loop index value on the stack which is then used by BLINK so that each time through the loop the index will increment up to the limit of 8 and exit the loop. This means that the index I will go through the values 0,1,2,3,4,5,6,7 branching back each time to just after DO but once it reaches 8 it does not loop back but continues after the word LOOP which in the case of the example executes an automatic EXIT or return operation back to the console ready for more input.
You can customise this simple 8 LED blinker by having a different blink rate for each LED like this: 8 0 DO I PIN I 2 + HZ LOOP
Now that creates an interesting pattern with the interaction between the LEDs.
TAQOZ# MUTE ok TAQOZ# 8 0 DO I PIN I 2 + HZ LOOP ok |
NEW WORDS
WORD | STACK | DESCRIPTION |
WORDS | Display all the dictionary words (includes user words) | |
BLINK | ( pin -- ) | Select the smartpin and make it blink slowly |
PIN | ( pin -- ) | Select a smartpin to use |
HZ | ( n -- ) | Configure the preselected smart pin as an NCO set to n Hz |
MUTE | Mute or stop the preselected smartpin | |
DO | ( limit start -- ) | Push limit and start index and loop start address onto the loop stack. |
LOOP | Increment the loop index and loop back while index <> limit | |
I | ( -- index ) | Push the current loop index value onto the stack |
STACK BASICS
You may find Forth style RPN "back-to-front" from conventional languages but that doesn't mean either one is better. Do you know that we use RPN in our speech? Everyone knows it takes 60 seconds to make 1 minute. Wait a minute, where's the RPN? Well in most computer language "styles" we might express that as "minute(1) = seconds(60)" so the latter is more mathematical but the former is more natural for English speakers. Where it doesn't seem natural is when we have more than one parameter as in adding numbers. Try this: 8 4 + which performs the addition after it has the parameters with the result left on the stack. After all though, you can't bake a cake before you have both eggs and flour available, just like Forth parameters where we bake the 8 and 4 with a + operation. You can type a single tiny dot . to print (and use) the number off the stack or you can type .S to view the stack.
TAQOZ# 8 4 ok TAQOZ# .S DATA STACK (2) 1 $0000.0004 4 2 $0000.0008 8 ok TAQOZ# + ok TAQOZ# .S DATA STACK (1) 1 $0000.000C 12 ok TAQOZ# |
NEW WORDS
WORD | STACK | DESCRIPTION |
+ | ( n1 n2 -- n3 ) | Add (and use) n1 and n2 and push the result n3 back onto the data stack |
.S | Print and view the contents of the data stack non-destructively | |
. | ( n -- ) | Print the signed value in the current number base (default decimal) |
BLINKING LEDs AGAIN
Making the LED blink the hard old way we need to stop those blinking LEDs first but they won't stop blinking after a reset or even a cold start (type ^Z). We could power it back up or we could use Forth to do the work for us: 8 0 DO I PIN MUTE LOOP
Now that we shut them up here's the old way in Forth, but it is only a single line: BEGIN 5 HIGH 100 ms 5 LOW 100 ms AGAIN
That works but now it is locked up in an endless loop bounded by BEGIN AGAIN and the console doesn't respond!!! Don't panic, just hit escape four times in a row to get back to the console. If I wanted any key to stop the looping I could have typed: BEGIN 5 HIGH 100 ms 5 LOW 100 ms KEY UNTIL
KEY returns a character from the serial buffer and pushes the value onto the stack just like any other value. When the buffer is empty KEY returns a null value or the value 0. UNTIL uses a value off the top of the stack as a true/false flag and will continue looping until the value it true. In most languages 0 is false but any non-zero value can be true and such is the case with our BEGIN UNTIL loop.
NEW WORDS
WORD | STACK | DESCRIPTION |
BEGIN | Mark (no code) the beginning of a BEGIN loop (no indexing or automatic count) | |
AGAIN | Jump back to the first word after BEGIN | |
UNTIL | ( flg -- ) | Until the flg is true loop back |
HIGH | ( pin -- ) | Drive the pin high (automatically sets the direction register) |
LOW | ( pin -- ) | Drive the pin low (automatically sets the direction register) |
ms | ( n -- ) | Wait for n milliseconds |
KEY | ( -- char ) | Return with the next character in the console buffer or else a value of $00 |
DEFINING NEW WORDS
Forth is like tofu, it takes on the flavor of what you add to it and so the language can be extended with new definitions/functions referred to as words which are maintained in a dictionary. The dictionary is the goto place for when you are typing into the console and if what you type is not a number it will search the dictionary for a match and when found it looks up the code address field in that entry and compiles this, either as an interactive temporary compilation or as part of a new word being defined. Note though that some words are never normally compiled as they are directives needed to control the compiler and to create structures. These are referred to as immediate words (but even immediate words can be forced to compile using other immediate words!)
OVER
Now let's take the bit of code we used to create a blinking LED in software and create a new word called "BLINKY" like this:
: BLINKY BEGIN 5 HIGH 100 ms 5 LOW 100 ms KEY UNTIL ;
Remember to use at least one space between ALL words including : and ;
The name BLINKY has been added to the dictionary (you can type WORDS to check if you like) and so if we type BLINKY it will do just so.
TAQOZ# : BLINKY BEGIN 5 HIGH 100 ms 5 LOW 100 ms KEY UNTIL ; ok TAQOZ# BLINKY |
Now BLINKY is "hard wired" to P5 and 100 ms delay but we could define a better BLINKY that takes a pin number and a delay value like this:
: BLINKER ( pin ms -- ) BEGIN OVER HIGH DUP ms OVER LOW DUP ms KEY UNTIL 2DROP ;
Welcome to manipulating the stack using DUP OVER and 2DROP (which is simply DROP DROP) etc.
TAQOZ# : BLINKER ( pin ms -- ) BEGIN OVER HIGH DUP ms OVER LOW DUP ms KEY UNTIL 2DROP ; ok TAQOZ# 2 20 BLINKER |
If you want to run code in another cog then is a very quick and simple example to run it in COG#1 as a task. First however we need to run a new instance of TAQOZ in another cog (originally they were all loaded but the latest version doesn't automatically load them).
In this example we will choose cog#1 using 1 NEWCOG
TAQOZ# 1 NEWCOG ok |
Now that COG#1 has been loaded with a TAQOZ kernel it is sitting in a IDLE loop waiting for a task to perform. All we need to do is to give it the address of that task. To make BLINKER run continuously the blinking code is placed in a BEGIN AGAIN structure which will loop unconditionally after which we can find the address of BLINKER (using the tick symbol $27) and store it where cog#1 will be checking in its task variable.
TAQOZ# : BLINKER BEGIN 7 HIGH 100 ms 7 LOW 100 ms AGAIN ; ok TAQOZ# ' BLINKER 1 TASK W! ok |
The last line here I would pronounce as "address of - BLINKER - 1 TASK - W STORE"
NEW WORDS
WORD | STACK | DESCRIPTION |
: | Read in the following word and create a new word in the dictionary Does not execute any code while defining | |
; | Compile an EXIT word and ends definition - does not execute | |
EXIT | Exit word by returning to implicit caller | |
OVER | ( n1 n2 -- n1 n2 n1 ) | Duplicate the second stack item, that is push a copy over the top. |
DUP | ( n1 -- n1 n1 ) | Duplicate the top stack item (copies and pushes) |
DROP | ( n1 -- ) | Drop the top stack item (pops and discards) |
2DROP | ( n1 n2 -- ) | Drop 2 stack items (pops the stack twice) |
( | Stack comment up to trailing ) is ignored by the compiler (<--whitespace!) | |
' | ( -- address ) | Find the CFA (code field address) of the word following the tick. |
W! | ( n address -- ) | Store n as a 16-bit word at address. (wrword tos1,tos) |
TASK | ( cog# -- address ) | Index the cog's task variable and return with its address. |
Q: What code is compiled when we define a new word?
In the example of blinker we can look at the code compiled by finding the code address using the tick word which will leave the CFA (code field address) on the stack from which we can use that address and dump it in a suitable format:
TAQOZ# ' BLINKER $20 DUMPW 00.102C: 0074 0175 0071 D7C8 0074 0177 0071 D7C8 t.u.q...t.w.q... 00.103C: E0DA FC8A 0066 0061 013E 102C FE20 D892 ....f.a.>.,. ... ok |
Substituting names for wordcodes
00.102C: OVER HIGH DUP ms OVER LOW DUP ms t.u.q...t.w.q... 00.103C: KEY UNTIL 2DROP EXIT 013E 102C FE20 D892 ....f.a.>.,. ... ok |
UPDATE - using the full version of TAQOZ which includes a decompiler we can see how it is constructed, although the addresses will be different:
TAQOZ# SEE BLINKER 1BC1B: pub BLINKER 0ABB4: 1807 7 0ABB6: 0173 HIGH 0ABB8: 1864 100 0ABBA: 0FB4 ms 0ABBC: 1807 7 0ABBE: 0175 LOW 0ABC0: 1864 100 0ABC2: 0FB4 ms 0ABC4: ABB5 BLINKER ; ( 18 bytes ) |
16-bit word size was chosen with DUMPW for 32 bytes and the first wordcode is $0074 which is the cog address of OVER. In fact any wordcode that is less than $400 is a cog/lut address which is executed by the wordcode interpreter loop. But $D7C8 is not a cog address but points to more wordcode. The $0074 following it is the same as the first wordcode which is OVER. We can use the tick symbol word to find the CFA of OVER to confirm this:
TAQOZ# ' OVER .W $0074 ok |
Therefore, each 16-bit wordcode has a direct correspondence with a word in the BLINKER definition. If you follow it through past 2DROP ($0066) you will find that $0061 is not the ; word used to complete the definition, it is in fact the wordcode for EXIT since ; is an immediate word that tells the compiler to complete the definition and compile an EXIT. Wordcodes such as FC8A are compact wordcodes that are decoded further and in this case $FC8x means conditional branch backwards by n words. So $0A = to 10 words back from the next instruction which brings us back to the beginning.
Now as for the name BLINKER itself it is kept in the dictionary which is made up of a contiguous list of names that include the count byte and attributes, and the wordcode for the name. To find that entry use NFA' instead.
TAQOZ# NFA' OVER $10 DUMP 00.B406: 04 4F 56 45 52 74 00 04 53 57 41 50 7B 00 03 52 .OVERt..SWAP{..R ok |
MORE SMARTPIN FUN
PWM is used everywhere from power supplies to motor speed control and even generating analog waveforms. By varying the on time of a repetitive pulse to the off time we can vary the average voltage (thereby power) which may be eventually filtered by mechanical motion, thermal response, or by a filter of some kind. The ratio of on to off time is called the duty cycle meaning that a square wave that has equally on and off has 50% duty cycle and can only deliver an average of 50% of the voltage whereas an on time of 1 in 4 would deliver an average of 25%. If you connect a heating element via a suitable driver you can vary the amount of heat simply by controlling the duty cycle and if the repetitive frequency is high enough the thermal response "filters" these pulses. Slow it right down from hundreds or thousands of times per second to many seconds and those pulses of heat will no longer be filtered or averaged out. Similarly we can generate a voltage by filtering a PWM signal with a simple resistor and capacitor filter so that 0% duty outputs 0V and 100% duty outputs the full signal voltage (normally 3.3V) but 25% will produce 825mV. PWM is used in place of linear control because it can be controlled digitally and without the losses that result from "restricting" the power, a bit like putting your hand on an electric motor to try and slow it down!!.
The smartpins support a few different modes of PWM but if we have an LED connected to a pin we should be able to make it very dim with a 10% duty cycle PWM. This is how we can do it in TAQOZ after we have selected a pin to use, in this case 5 PIN and then 10 100 10 PWM which specifies on time in clock cycles, overall frame time (that is, the combined on and off time), and the frequency in clock cycles which should be fast enough to be filtered effectively, in this case by persistence of vision in the human eye. The example given uses a frame of 100 cycles just so that the on time can be expressed as a percentage. The longer the frame time the greater the resolution, but also the lower the repetitive frequency which is also prescaled by the last parameter.
Try different values but lastly try making the frequency value very high for a very slow frequency, slow enough that POV filtering becomes ineffective. Try 10 100 10000 PWM and you will see the LED pulse and now it seems brighter even though the duty cycle hasn't changed. Drop back to 1% with 1 100 10000 PWM and it will still flicker the same rate but be much dimmer.
Make a new name for our dimmer as part of Forth and use it:
TAQOZ# : DIMMER ( value pin -- ) PIN 100 10 PWM ; ok TAQOZ# 5 6 DIMMER ok TAQOZ# 20 6 DIMMER ok TAQOZ# 20 5 DIMMER ok |
We could have elected to not specify a pin directly but leave it out like the other examples and perhaps that would be more consistent like this instead:
TAQOZ# : DIMMER ( value -- ) 100 10 PWM ; ok TAQOZ# 4 PIN 50 DIMMER ok TAQOZ# 5 PIN 1 DIMMER ok TAQOZ# 5 DIMMER ok |
Lastly in this console capture we setup 4 pins to output 4 different rates from 20 to 80% duty cycle with the resultant waveforms
TAQOZ# 5 PIN ok TAQOZ# 10 100 10 PWM ok TAQOZ# 10 100 10000 pwm ok TAQOZ# 1 100 10000 PWM ok TAQOZ# 4 0 DO I PIN I 1+ 20 * 100 10 PWM LOOP ok |
Another mode that is very useful is being able to generate an exact number pulses with controlled high and low times.
We can do it like this and show it alongside our previous PWM pulses for comparison.:
TAQOZ# 0 PIN 1000 3000 HILO 3 PULSES ok |
After specifying HILO we can play with 2 or 10 or 100 pulses etc.
NEW WORDS
WORD | STACK | DESCRIPTION |
PWM | ( on frame freq -- ) | Enable PWM that turns on for on cycles, off for frame-on, and freq as a prescaler (more=slower) |
HILO | ( high low -- ) | Specify High and Low times for subsequent PULSE or PULSES words |
PULSES | ( cnt -- ) | Generate cnt pulses according to specified high and low times. |
PULSE | Sames as PULSES but only one pulse | |
' | <name> ( -- cfa ) | This immediate acting word finds the code address of the following word and leaves the value on the stack |
NFA' | <name> ( -- nfa ) | This immediate acting word finds the address of the word in the dictionary |
.W | ( n -- ) | Print n as a 16-bit formatted hex word. |
SMARTPINS AS ASYNCHRONOUS SERIAL UARTS
One liners are handy but extending TAQOZ with your own words is easy too.
Here I set pin 2 as a 20M baud UART transmit line, create a transmit string word
so that I can pass the string and the pin number to it, and of course I test it.
TAQOZ# : TRANSMIT$ ( string pin -- ) PIN DUP LEN$ TXDAT ; ok
TAQOZ# 2 PIN 20 M TXD ok
TAQOZ# " HELLO WORLD" 2 TRANSMIT$ ok
(20M was the limit of the scope's decoder)
Exercise:
Create a new word that allows a string to be sent on any pin at any baudrate. (assume P2 clock of 180MHz)
Usage:
" Hello World" 115,200 8 SEND$
" Parallax P2 Smart Pins are fast and fun! " 20,000,000 9 SEND$
" Can any other micro do this?" 90,000,000 10 SEND$
Solution: (drag mouse through hidden text in box to reveal and copy)
: SEND$ ( string baud pin -- ) PIN TXD DUP LEN$ TXDAT ; |
<WORK IN PROGRESS>
The new section is here while this section at the end of the TAQOZ document will be removed (just pending some checks on updates)
There are 432 words in TAQOZ ROM, for mere humans too much to memorise. So it’s a good idea to keep this glossary by you when programming. The words are grouped under headings like Maths, Stack Operations to speed up searches.
The experienced Forth programmer will notice many words that are unique to TAQOZ. TAQOZ is not intended to meet any international standard; and for a good reason. The non-standard word set is a very close fit around the unique P2 processor and its special circuits. This results in a smaller, more useful and efficient a tool to program P2. TAQOZ is also close enough to many Forth dialects to be quickly assimilated, aided by the glossary.
About the format of the word tables
The WORDS column shows TAQOZ words, with associated words grouped together
The STACK column shows the effect each word has on the data stack
The DESCRIPTION column briefly describes what the word does
The CONSOLE EXAMPLES column(s) show the TAQOZ prompt and user input followed by the response and a final "ok" as indication of acceptance. It may be confusing therefore for those unfamiliar with Forth-like languages as to what constitutes user input and what constitutes the response. Rather than explaining all this the user input is colored in as a guide.
Sample word description
Here this row covers both the FOR and the NEXT word since they are used together to form a simple loop.
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
FOR NEXT | ( cnt -- ) ( -- ) | Simple counted loop FOR | TAQOZ# CRLF 3 FOR ." HELLO! " NEXT HELLO! HELLO! HELLO! ok |
Data is referred to as bytes (8 bits), words (16 bits), longs (32 bits) and doubles (64 bits).
Boolean values of FALSE and TRUE are simply 0 or -1 ($FFFF_FFFF) although in fact any non-zero value can be accepted as a boolean true but not necessarily suitable for bitwise operations such as AND. For instance 4 IF and 8 IF would both resolve as true however 4 8 AND IF fails. Use 0<> to promote any non-zero to a full TRUE in such cases.
LIST OF DEBUGGING CONTROL KEY SHORTCUTS
ESC ESC ESC ESC ^C ^W ^D ^Q ^S ^B ^X ^Z ^? | Reset (works even if TAQOZ is not checking the console) Discard the current console line input Reset List WORDS in dictionary LSD Debugger in ROM (use ESC<CR> to return to TAQOZ) Print data stack Clear data stack Block memory dump of first 64kB Re-eXecute last line (very useful) Cold start Zap - wipe new code and dictionary - restore settings DEBUG List registers, current code an dictionary memory, and I/O |
DEBUGGING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
@WORDS | ( -- adr ) | Returns adr, the start of the dictionary | TAQOZ# @WORDS $20 DUMP 0B3F9: 04 44 45 4D 4F 00 10 03 44 55 50 73 00 04 4F 56 '.DEMO...DUPs..OV' 0B409: 45 52 77 00 04 53 57 41 50 7E 00 03 52 4F 54 82 'ERw..SWAP~..ROT.' ok |
WORDS | ( -- ) | List the current dictionary of words ^W shortcut | |
DUMP: | Use the following three words for byte, word, and long memory reads during a subsequent DUMP | pub SD DUMP: SDC@ SDW@ SD@ ; 0 $100 SD DUMP | |
DUMP DUMPW DUMPL DUMPA DUMPAW | ( adr bytes -- ) | Dump selected memory as:- bytes and ASCII words and ASCII longs and ASCII 64 ASCII characters wide (no hex) 128 ASCII characters wide (no hex) | TAQOZ# $FC000 $20 DUMP --- 000F_C000: 00 08 80 FF 3F 00 0C FC 32 C8 06 F6 1F 04 DC FC '....?...2.......' 000F_C010: 40 7E 74 FD 01 CA A6 F0 1F CA 26 F4 00 CA 62 FD '@~t.......&...b.' ok3 TAQOZ# $FC000 $20 DUMPL --- 000F_C000: FF80_0800 FC0C_003F F606_C832 FCDC_041F '....?...2.......' 000F_C010: FD74_7E40 F0A6_CA01 F426_CA1F FD62_CA00 '@~t.......&...b.' ok |
QD QW | ( adr -- ) | Quick Dump or Quick dump Words. List two lines of memory content | TAQOZ# : DEMO 10 2* . ; ok TAQOZ# ' DEMO QW 01000: F80A 00AE DA82 0063 013F 1000 D88A 0063 '......c.?.....c.' 01010: 0063 0000 0000 0000 0000 0000 0000 0000 'c...............' ok |
COG LUT RAM SD SF | ( -- ) | Modifies the next DUMP to use COG or LUT memory instead of the default HUB RAM. Resets to RAM after each DUMP | TAQOZ# 0 4 COG DUMP 00.0000: FDA0.0990 FD90.00BC 0000.0000 0000.0000 ok |
.S | ( -- ) | List the contents of the data stack Also accessible as the ^Q shortcut | TAQOZ# $CAFEBABE DUP $FFFF AND SWAP 16 >> .S DATA STACK x2 1 $0000.CAFE 51966 2 $0000.BABE 47806 ok |
DEBUG | ( -- ) | Dumps the various stacks, task registers, and quick dumps of sections of the current code and dictionary memory. ^? shortcut | |
lsio | ( -- ) | List the I/O, displays all 64 I/O pins and indicates what impedance the P2 senses on each pin | TAQOZ# lsio --- P:00000000001111111111222222222233333333334444444444555555555566 P:01234567890123456789012345678901234567890123456789012345678901 =:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ok |
LAP .LAP .ms CNT@ LAP@ | ( -- ) ( -- ) ( -- n ) ( -- n ) | Time the code between each LAP and report results with .LAP Report only milliseconds CNT@ reads the 32-bit CNT register LAP@ is used internally by .LAP | TAQOZ# LAP 1000 FOR NEXT LAP .LAP --- 32146 cycles = 401.825us ok TAQOZ# LAP 1000 FOR NEXT LAP .ms --- 401.825us ok |
RESET | ( -- ) | Reset the P2 chip ^C or <break> shortcut |
THE STACKS
TAQOZ is based on Tachyon Forth which maintains a data stack in LUT memory in each cog but keeps a copy of the top four elements in fixed registers in cog memory for quick and efficient access. Therefore there are no (ugly) PICK and ROLL operators so as to encourage clean and efficient coding using a minimum of stack manipulation. The "top of stack" element or the last one pushed etc is referred to simply as "tos". The stack is a last in, first out store. Functions that require and pass parameters drawn from the top of the stack to usually no more than a depth of four elements.
TAQOZ has a return stack in which code return addresses are stored, so that words can be nested within words within words and so on to make a complete program.
In addition to the data and return stack, there is a dedicated loop parameter stack and a branch stack used internally by looping structures. Since the return stack is only used for return addresses we can then also access loops and parameters from any section of code that is not part of that loop.
In Forth source code, the data stack effect of a word is shown within ( ) comment braces. e.g. ( n -- w c ) means a word consumes an ‘n’ long from the stack when it executes, leaving a ‘w’ word and ‘c’ byte or character on the stack afterwards.
The stacks store everything as 32 bit ‘longs’. In the stack effect comments, the following letters mean:-
n - signed long ( 32 bit )
u - unsigned long ( 32 bit )
w - signed word ( 16 bit, sign extended to 32bits )
uw - unsigned word (16 bit, padded with leading 0s to 32 bits )
c - byte or char ( 8 bit, padded with leading 0s to 32 bits )
d - signed double ( 64 bit, stored as two longs on the stack, m.s. half nearest tos )
ud - unsigned double ( 64 bit, stored as two longs on the stack, m.s. half nearest tos )
adr - 20 bit address
str - the address of a zero delimited string
Other mnemonics are used where useful, but are generally self-explanatory
STACK OPERATIONS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
!SP | ( -- ) | Initialise the data stack to empty Shortcut: ^S | ||
2DUP | (n1 n2 -- n1 n2 n1 n2 ) or ( d -- d d ) | Make a copy of a double at tos | ||
DUP OVER 3RD 4TH | ( n -- n n ) ( n1 n2 -- n1 n2 n1 ) ( n1 n2 n3 -- n1 n2 n3 n1 ) ( n1 n2 n3 n4 -- n1 n2 n3 n4 n1 ) | Make a copy of the 1st, 2nd, 3rd, or 4th tos | TAQOZ# 1234 5678 9012 3456 .S DATA STACK x4 1 $0000.0D80 3456 2 $0000.2334 9012 3 $0000.162E 5678 4 $0000.04D2 1234 ok | TAQOZ# 4TH .S DATA STACK x5 1 $0000.04D2 1234 2 $0000.0D80 3456 3 $0000.2334 9012 4 $0000.162E 5678 5 $0000.04D2 1234 |
?DUP | ( n1 -- n1 n1 ) else ( n -- n ) | Duplicate the tos only if it is non-zero | TAQOZ# 1234 ?DUP 0 ?DUP .S DATA STACK x3 1 $0000.0000 0 2 $0000.04D2 1234 3 $0000.04D2 1234 ok | |
DEPTH | ( -- n ) | n = the current depth of the data stack ( not including n ) | TAQOZ# 1 2 3 ok | TAQOZ# DEPTH . 3 ok |
DUPC@ | ( addr -- addr byte ) | Duplicate the address and read a byte | ||
DROP 2DROP 3DROP | ( n -- ) ( n1 n2 -- ) or ( d -- ) ( n1 n2 n3 -- ) | Drop and discard the top elements of the data stack, either 1, 2, or 3 levels | TAQOZ# 1234 5678 9012 .S DATA STACK x3 1 $0000.2334 9012 2 $0000.162E 5678 3 $0000.04D2 1234 ok | TAQOZ# 2DROP .S DATA STACK x1 1 $0000.04D2 1234 ok |
NIP | (n1 n2 -- n2 ) | Nip out the second element | TAQOZ# 1234 5678 9012 NIP .S DATA STACK x2 1 $0000.2334 9012 2 $0000.04D2 1234 ok | |
SWAP | (n1 n2 -- n2 n1 ) | Swap the top two data elements | TAQOZ# 1234 5678 .S DATA STACK x2 1 $0000.162E 5678 2 $0000.04D2 1234 ok | TAQOZ# SWAP .S DATA STACK x2 1 $0000.04D2 1234 2 $0000.162E 5678 ok |
2SWAP | ( n1 n2 n3 n4 -- n3 n4 n1 n2 ) or ( d1 d2 -- d2 d1 ) | Swap the top two doubles | ||
ROT | ( n1 n2 n3 -- n2 n3 n1 ) | Rotate the 3rd element to the top | TAQOZ# 1234 5678 9012 .S DATA STACK x3 1 $0000.2334 9012 2 $0000.162E 5678 3 $0000.04D2 1234 ok | TAQOZ# ROT .S DATA STACK x3 1 $0000.04D2 1234 2 $0000.2334 9012 3 $0000.162E 5678 ok |
-ROT | ( n1 n2 n3 -- n2 n1 n2 ) | Rotate the top element to the 3rd position (Reverse ROT) | TAQOZ# 1234 5678 9012 .S DATA STACK x3 1 $0000.2334 9012 2 $0000.162E 5678 3 $0000.04D2 1234 ok | TAQOZ# -ROT .S DATA STACK x3 1 $0000.162E 5678 2 $0000.04D2 1234 3 $0000.2334 9012 ok |
>L L> | ( n -- ) ( -- n ) | Push tos onto the loop stack Pop n from the loop stack | ||
>R R> | ( n -- ) ( -- n ) | Push n onto the return stack Pop n from the return stack |
Note: The data stack is 4 levels deep in the cog and then implemented as a non-addressable LIFO stack in cog memory. TAQOZ words are optimized for these four fixed cog registers and to encourage efficient stack use no messy PICK and ROLL words are implemented. There are many words that also avoid pushing and popping the stack as this slows execution speed too. Try to factor words so that they use four or less parameters.
LOGIC (BITWISE)
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
AND ANDN OR XOR | (n1 n2 -- n3 ) (n1 n2 -- n3 ) (n1 n2 -- n3 ) (n1 n2 -- n3 ) | n3 = n1 and n2 n3 = n1 and not n2 n3 = n1 or n2 n3 = n1 xor n2 | TAQOZ# 1011b 101b AND .BIN %1 ok TAQOZ# 1011b 101b NAND .BIN %1010 ok TAQOZ# %1000 %11 OR .BIN %1011 ok TAQOZ# %1010 %11 XOR .BIN %1001 ok |
NOT | ( n1 -- n2 ) | n2 = n1 all bits inverted | TAQOZ# %11 NOT .BIN %11111111111111111111111111111100 ok |
SHIFTING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
>> << | ( n1 cnt -- n2 ) | Shift n1 right or left by cnt bits | TAQOZ# $F 2 >> . 3 ok TAQOZ# 3 2 << . 12 ok |
ROL ROR | (n1 cnt -- n2 ) (n1 cnt -- n2 ) | n2 = rotate n1 left cnt times, with the ms bit rotating into the ls bit n2 = rotate n1 right cnt times, with the ls bit rotating into the ms bit | TAQOZ# $F0000000 2 ROL HEX . $C0000003 ok TAQOZ# $15 3 ROR HEX . $E0000001 ok |
SAR | ( n1 cnt -- n2 ) | Shift n1 cnt places right, arithmetically | TAQOZ# -255 4 SAR . -31 ok |
2/ 4/ 8>> 9>> 16>> | ( n1 -- n2 ) | Shift tos right by 1, 2, 8, 9, or 16 ** ( 2/ is equivalent to 1 >> ) | TAQOZ# 32 4/ . 8 ok |
2* 4* 8<< 9<< | ( n1 -- n2 ) | Shift tos left by 1, 2, 8, or 9 ** | TAQOZ# 3 8<< . 48 ok |
** These instructions work with a single operand and return with a single operand. Because they do not push or pop they are useful, being both faster and more compact. For instance the word 8>> might simply be equivalent to 8 >> except it only needs a single wordcode and saves time by not having to push 8 and then pop it to discard afterwards since it is essentially a single PASM instruction.
CONVERSION AND MASKING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
>9 | ( n -- 9bits ) | 9bits = n and $1FF | |
>B | ( n -- byte ) | byte = n and $FF | |
B>L | (a b c d -- dcba ) | Merge four bytes into a long | |
B>W | ( a b -- word ) | Merge two bytes into a word | |
W>L | ( word1 word2 -- long ) | Merge words into a long ( word1 word2 -- long ) | |
W>B | ( word - - wordl wordh ) | Split word into two bytes | |
L>W | ( long -- wordl wordh ) | Split long into two words | |
>N | ( n -- nibble ) | nibble = n and $F ( n -- nibble ) | |
>W | ( n -- word ) | word = n and $FFFF | |
BITS | ( n1 cnt -- n2 ) | Extract l.s. cnt bits from n1 | TAQOZ# $F0F0F0F0 5 BITS . 32 ok |
L>S | ( n -- lsb9 h ) | Specialized operation for file system addresses | |
REV | ( n1 -- n2 ) | Reverse bits 0..32 -> 32..0 ( n1 -- n2 ) Very useful for array address twiddling in fast fourier transforms | TAQOZ# 5 REV . 167772160 ok |
>| | ( bitmask -- bitpos ) | Encode - bitpos = the position of the msb set in bitmask | TAQOZ# %0100 >| . 2 ok TAQOZ# 64 >| . 6 ok |
|< | ( bitpos -- bitmask ) | Decode - bitmask = long with one bit set at bitpos position | TAQOZ# 3 |< .BIN %1000 ok TAQOZ# 6 |< .BIN %1000000 ok |
S># | ( str -- u digits | false ) | Converts string at str to a number u and digit count digits, else returns false on fail |
MATHS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
*/ | ( n1 n2 n3 -- res ) | Multiply n1 by n2 and divide the 64-bit product by n3 for a 32-bit result | TAQOZ# 355 100000000 113 */ . --- 314159292 ok |
+ - * / | (n1 n2 -- n3) | Add, subtract, multiply, divide | TAQOZ# 2 3 + . 5 ok TAQOZ# 12 -4 / . -3 ok |
ABS | ( n1 -- n2 ) | Absolute value of n1 - if n1 is negative then negate it to a positive number | TAQOZ# 4 ABS . 4 ok TAQOZ# -33 ABS . 33 ok |
BOUNDS | ( n1 n2 -- n1+n2 n1 ) | Add n1,n2 leaving n1 at tos | |
MAX MIN | ( u1 u2 -- u3 ) | Leave the maximum, minimum of u1 and u2, all unsigned | TAQOZ# 4 5 MAX . 5 ok TAQOZ# 33 99 MIN . 33 ok |
MAXS MINS | ( n1 n2 -- n3 ) | Leave the maximum, minimum of n1 and n2, all signed | |
NEGATE -NEGATE ?NEGATE | ( n1 -- n2 ) ( n1 n2 -- n3 ) ( n1 flag -- n2 ) | n2 = -n1 n3 = -n1 if n2 is negative, else n3=n1 n2 = -n1 if flag is true, else n2=n1 | TAQOZ# 45 NEGATE . -45 ok TAQOZ# 54 -2 -NEGATE . -54 ok TAQOZ# -2 TRUE ?NEGATE . 2 ok |
GETRND | ( -- n ) | Returns pseudorandom number * | |
RND | ( -- n ) | Generate n, a pseudo-random long enhanced with the system counter | |
SQRT | ( u1 -- u2 ) | u2 = unsigned square root of u1 | TAQOZ# 9 3 3 * * SQRT . 9 ok |
UM* | ( u1 u2 -- ud1 ) | Unsigned u1 * u2, resulting in unsigned 64 bit ud1 | |
UM// | ( ud1 u2 -- rem quot ) | Unsigned 64 bit ud1 divided by long u2 resulting in rem and quotient unsigned longs | |
U/ U// | ( u1 u2 -- u3 ) ( u1 u2 -- rem quot ) | Divide u1 by u2, all unsigned Divide u1 by u2, all unsigned | |
W* | ( w1 w2 -- n ) | n = w1*w2 (fast single PASM instruction) | |
1+ 2+ 4+ | ( n1 -- n2 ) | Increment the tos element by 1, 2, or 4 * | TAQOZ# 1234 2+ . 1236 ok |
1- 2- | ( n1 -- n2 ) | Decrement the tos element 1 or 2 * | TAQOZ# 1234 2- . 1232 ok |
* These instructions work with a single operand and return with a single operand. Because they do not push or pop they are useful, both faster and more compact. For instance the word 1+ might simply be equivalent to 1 + except it only needs a single wordcode and saves time by not having to push 1 and then pop it to discard afterwards since it is essentially a single PASM instruction.
MODIFIERS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
KB MB M | n1 -- n2 n1 -- n2 n1 -- n2 | n2 = n1*1,024 n2 = n1*1,048,576 n2 = n1*1,000,000 | TAQOZ# 128 KB . --- 131072 ok |
STRINGS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
[“] | ( -- ) | During compilation, displays the following string in the input stream until a terminating “ | |
“ | ( -- str1 ) | Compile a string in the input stream up to the trailing " and leave the address of the null terminated string on the stack. | TAQOZ# " hello world" $10 DUMP --- 01002: 68 65 6C 6C 6F 20 77 6F 72 6C 64 00 10 F8 44 D8 'hello world...D.' ok |
$! | ( str1 str2 -- ) | Store string at hub memory address str1 at address str2 | TAQOZ# 16 BYTES mystring ok TAQOZ# “ Hello” mystring $! ok |
GET$ | ( -- str ) | Build a delimited word in wordbuf for wordcnt and return immediately upon a valid delimiter | |
LEN$ | ( str - n ) | n = length of string at addr str | TAQOZ# mystring LEN$ . 5 ok |
NULL$ | ( -- str ) | An empty string at address str | |
$= | ( str1 str2 -- flag ) | flag = true if string at hub memory address str1 is identical tostring at str2, else flag = false | TAQOZ# mystring mystring $= . -1 ok |
$># | ( str -- n digits ) or ( str -- flag ) | Try to convert string at address str to number n and digits else return flag = false | TAQOZ# " $123.456" $># . SPACE .L --- 6 $0012_3456 ok |
Note: All TAQOZ strings are 0 delimited
DEFINING NEW VARIABLES
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
byte <variable name> word <variable name> long <variable name> | ( -- ) ( -- ) ( -- ) | Define new byte variable Define new word variable Define new long variable | TAQOZ# --- defining vars TAQOZ# byte MYBYTE ok TAQOZ# word TIMEOUT ok TAQOZ# long SAMPLE ok | TAQOZ# --- writing to a long var. TAQOZ# 2000 SAMPLE ! ok TAQOZ# --- reading a long var. TAQOZ# SAMPLE @ . 2000 ok |
bytes <variable name> words <variable name> longs <variable name> | ( n -- ) ( n -- ) ( n -- ) | Define new byte array variable Define new word array variable Define new long array variable All these create an array of size n bytes, words or longs | TAQOZ# 4 longs BUFFER ok | TAQOZ# pub INITBUFFER ( -- ) TAQOZ# --- set all entries to 100 TAQOZ# 4 0 DO TAQOZ# 100 BUFFER I 4* + ! TAQOZ# LOOP ; ok |
org res | ( adr -- ) ( n -- ) | Set data pointer used for defining variables to hub memory adr ( for example to reclaim data space when reloading source code) Takes next word in input stream as a … and reserves n bytes of hub memory | TAQOZ# IFDEF *MANDELBROT* TAQOZ# Cx org TAQOZ# --- Cx is the 1st variable defined in the code below this TAQOZ# FORGET *MANDELBROT* TAQOZ# } ok TAQOZ# 8 res fnam ok |
Note 1: When variables like BUFFER execute, they leave the address of the variable at tos ( -- adr )
Note2: Remember that addresses point to bytes in memory, so members of an array defined with ‘ longs’ have addresses spaced 4 apart, ‘words’ 2 apart, ‘bytes’ 1 apart. Hence the 4* word in the example above to calculate the long address.
Note 3: None of these words initialise the variable, they just allot the required memory.
DEFINING NEW CONSTANTS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
:= | ( n -- ) | Create a long constant using the name next in the input stream | TAQOZ# 1 := RED ok TAQOZ# RED . 1 ok TAQOZ# 2 ' RED ==! Ok TAQOZ# RED . 2 ok |
==! | ( n adr -- ) | Change the value of constant at adr to n |
DEFINING NEW LITERALS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
| | ( c -- ) | Compile byte c into code memory | |
|| | ( w -- ) | Compile word w into code memory | |
, | ( n -- ) | Compile long n into code memory |
PREDEFINED TASK VARIABLES
Each cog may have its own set of variables that are offset from a base address ( that address is obtained by using REG - see below )
This is so that any cog running TAQOZ may have different I/O devices selected etc.
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
REG | ( index -- adr ) | Find the address of register ‘index’ | |
delim | ( -- adr ) | Word delimiter (normally space) plus backup byte with delimiter detected (SP,TAB,CR etc) | |
names | ( -- adr ) | Points to the start of the latest name field in the dictionary (builds down) | |
uemit | ( -- adr ) | Vector that points to cfa of current EMIT routine (0=console=(EMIT)) | |
ukey | ( -- adr ) | Vector that points to cfa of current KEY routine (0=console=(KEY)) |
Note: There are more registers available, the above are all that are defined in TAQOZ ROM. The user may create more words to access the extra registers with the aid of the ROM assembly code listing. TAQOZ RELOADED includes more words for register access too.
PREDEFINED CONSTANTS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
ON TRUE -1 OFF FALSE | ( -- n ) ( -- n ) | n = -1 n = 0 | TAQOZ# ON OFF .S DATA STACK x2 1 $0000.0000 0 2 $FFFF.FFFF -1 ok |
CLKHZ | ( -- n ) | n = 20,000,000 |
Note: Constants can be changed using ==!
DEFINING NEW WORDS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
[ ] | flag that we have entered a definition end definition and lock allocated bytes | |||
pub : | ( -- ) | Create new word as public | TAQOZ# pub HITHERE .” Hello World” ; ok TAQOZ# : HITHERE2 .” Hello World” ; ok | TAQOZ# HITHERE Hello World ok |
pri | ( -- ) | Create a new word that is marked private so a RECLAIM can remove them from the dictionary when required. The private words will still function, but can no longer be used to make new words. Keeps the dictionary uncluttered from unnecessary detail | TAQOZ# pri HIAGAIN .” Hello the World” ; ok | TAQOZ# HIAGAIN Hello the World ok TAQOZ# RECLAIM ok TAQOZ# HIAGAIN ?? hiagain not found ok |
pre | ( -- ) | Create a new word that has the "immediate" attribute set so that it executes immediately at compile time (words like IF ELSE THEN etc) | TAQOZ# pre <new word name> more TAQOZ words ; ok | |
; | ( -- ) | Finalise a new word | See examples above | |
ALLOT | ( bytes -- ) | Allot n bytes of code memory - advances "here" | ||
CREATE$ CREATE | ( str -- ) ( -- ) | Creates a new word in the dictionary using the string at address str. If string is empty no new word is created Creates a new word in the dictionary using the name that follows in the input stream | ||
CFA | ( nfa -- cfa ) | |||
CPA | ( nfa -- cpa ) | The CPA is the address of the word code stored in the name field header that points to the code to execute | ||
TAQOZ END | ( -- ) ( -- ) | Marks the start of a block of source code to be compiled in block mode Marks end of block load mode |
CONDITIONAL DEFINING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
IFDEF IFNDEF | ( -- ) | If the next name in the input stream is already DEFINED then process all input stream that follows up to the terminating curly brace } If next name in the input stream is NOT DEFINED then process all source between here and the matching curly brace } | TAQOZ# IFDEF *BREAKOUT* TAQOZ# bk org TAQOZ# FORGET *BREAKOUT* TAQOZ# } ok |
DICTIONARY
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
HERE | ( -- adr ) | Point to the next compilation location | ||
@CODES @DATA @WORD | ||||
@HERE | ( -- atradr ) | Point to the attribute byte in the header of the latest name | ||
@NAMES | ( -- namadr ) | Point to the latest entry in the names dictionary | ||
FORGET | ( -- ) | Takes next word in input stream, and forgets all names in the dictionary from this name onwards - from most recent instance of the name | ||
SEARCH | ( cstr -- nfaptr ) | Search the dictionaries for cstr which points to a counted word string constructed as count+string+null |
COMPARISON
WORDS | DESCRIPTION | CONSOLE EXAMPLES | |
0= 0<> 0< | ( n -- flag ) | Compare tos with zero | TAQOZ# 0 0= . -1 ok TAQOZ# 0 0<> . 0 ok |
= <> < > U< U> <= => | ( n1 n2 -- flag ) ( u1 u2 -- flag ) ( n1 n2 -- flag ) | Compare top two stack values Compare two unsigned longs Compare top two stack values | TAQOZ# 1 1 = . -1 ok TAQOZ# 1 1 <> . 0 ok |
WITHIN | ( val min max -- flag ) | Return with flag true if val is within min and max (inclusive, not ANSI) | TAQOZ# 3 1 5 WITHIN . -1 ok TAQOZ# 3 3 7 WITHIN . -1 ok TAQOZ# 67 3 7 WITHIN . 0 ok |
BRANCHING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
IF ELSE THEN | ( cond -- ) | Conditional execution IF | ||
BEGIN AGAIN UNTIL WHILE REPEAT | ( cond -- ) ( cond -- ) | Start a repeated conditional loop Repeat forever Repeat until condition false Repeat while condition true End a WHILE loop | BEGIN xxx AGAIN BEGIN xxx cond UNTIL BEGIN xxx cond WHILE yyy REPEAT | |
GOTO | ||||
EXIT | ( -- ) | Exit now | ||
0EXIT ?EXIT | ( cond -- ) | Exit on 0 / false, or on true |
CASE
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES | |
BREAK | ( -- ) | Stop executing the CASE code immediately and execute the code that follows | TAQOZ# pub CASETEST ( val -- ) TAQOZ# --- print ‘val’ as a word TAQOZ# SWITCH TAGOZ# 1 CASE .” one” BREAK TAQOZ# 2 CASE .” two” BREAK TAQOZ# 3 CASE .” three” BREAK ; ok TAQOZ# 2 CASETEST two ok TAQOZ# 1 CASETEST one ok | |
CASE | ( val1 -- ) | Execute the following code up to BREAK if val1 = SWITCH val2 | ||
SWITCH | ( val2 -- ) | Stores val2 ready for comparison with a number of CASEs | ||
CASE@ | ( -- val2 ) | Returns the value stored by the last SWITCH word | ||
CASE= | ( val -- flag ) | flag set true if val = value stored by the last SWITCH word | ||
CASE> | ( from to -- flag ) | flag set true if val stored by the last SWITCH word is within range ‘from’ - ‘to’, inclusive |
VECTORED EXECUTION
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
AUTO | |||
‘ | ( -- adr | false ) | Takes the next word in the input stream, looks it up in the dictionary and returns the execution adr of the word else if not found returns false | |
CALL | |||
JUMP | ( adr -- ) | jump to address on top of the data stack | |
NOP |
LOOPING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
DO ADO LOOP +LOOP | ( limit start -- ) ( start cnt -- ) ( -- ) ( -- ) | Execute code between DO LOOP until the index equals the limit. | TAQOZ# 8 0 DO I . LOOP 01234567 ok TAQOZ# 0 8 ADO I . LOOP 01234567 ok TAQOZ# 0 8 ADO I . 2 +LOOP 0246 ok |
FOR NEXT | ( cnt -- ) ( -- ) | Simple counted loop FOR | TAQOZ# CRLF 3 FOR ." HELLO! " NEXT HELLO! HELLO! HELLO! ok |
LEAVE | ( -- ) | Set the index to limit-1 so that it will LEAVE the loop at the next LOOP or +LOOP executed | |
?NEXT | ( flag -- ) | Exit FOR NEXT loop early if flag is true (non-zero) | |
I J | ( -- n ) ( -- n ) | I returns the current loop index J returns the index for the next outer loop. | |
I+ | ( n -- n+1 ) | Fast loop index e.g. to index through a table or array |
TIMING
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
us ms s | ( n -- ) | Wait for microseconds, milliseconds, or seconds | TAQOZ# CNT@ 100 ms CNT@ SWAP - . 8000552 ok |
CNT@ LAP .LAP LAP@ | Timing code between each LAP and report results with .LAP. CNT@ reads the 32-bit CNT and LAP@ is used internally by .LAP | TAQOZ# LAP 1234 5678 * LAP .LAP 242 cycles = 3.25us ok | |
ns | |||
WAIT | ( n -- ) | Wait for n clock cycles to pass |
MEMORY
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
ALIGN | ( adr1 n -- adr2 ) | adr2 = adr1 realigned on an n’th byte boundary | TAQOZ# 0 4 ALIGN . 0 ok TAQOZ# 4 4 ALIGN . 4 ok TAQOZ# 1 4 ALIGN . 4 ok TAQOZ# 5 4 ALIGN . 8 ok |
ERASE FILL | ( adr cnt -- ) (adr cnt c -- ) | Erase hub memory starting at adr for cnt bytes, filling with byte 0 Fill hub memory started adr for cnt bytes, with c byte | TAQOZ# $4.0000 $10 $FA FILL ok TAQOZ# $4.0000 $20 DUMP 04.0000: FA FA FA FA FA FA FA FA FA FA FA FA FA FA FA FA ................ 04.0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok |
CMOVE <CMOVE | ( src dst cnt -- ) ( src dst cnt -- ) | Move hub memory bytes starting from source to destination by cnt bytes Same as CMOVE, in reverse address order (Use one or the other when the two blocks overlap each other) | TAQOZ# @NAMES $4.0000 $20 CMOVE ok TAQOZ# $4.0000 $20 DUMP 04.0000: 03 54 52 49 80 86 1F 03 53 41 57 80 80 1F 03 44 .TRI....SAW....D 04.0010: 55 50 80 6B 00 04 32 44 55 50 80 6D 00 04 4F 56 UP.k..2DUP.m..OV ok |
C@ W@ @ | ( adr -- c ) ( adr -- w ) ( adr -- n ) | Fetch a byte, word, or long from hub memory | TAQOZ# @NAMES $10 DUMP 00.CFF2: 03 54 52 49 80 86 1F 03 53 41 57 80 80 1F 03 44 .TRI....SAW....D ok TAQOZ# @NAMES C@ .BYTE 03 ok |
C@++ | ( adr1 -- n adr2 | Fetch a byte from hub memory at adr1 and increment the address to adr2 leaving it tos | TAQOZ# $1000 C@++ .S DATA STACK x2 1 $0000.005C 92 2 $0000.1001 4097 ok |
C! W! ! | ( c adr -- ) ( w adr -- ) (n adr -- ) | Store a byte, word, or long to hub memory | TAQOZ# $DEADBEEF $F000 ! ok TAQOZ# $F000 $10 DUMP 00.F000: EF BE AD DE 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok |
C+! W+! +! | ( c adr -- ) ( w adr -- ) (n adr -- ) | Add parameter directly to hub memory location adr | TAQOZ# $600 $F000 +! ok TAQOZ# $F000 $10 DUMP 00.F000: EF C4 AD DE 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok |
COG@ COG! LUT@ LUT! | ( adr -- n ) ( n adr -- ) ( adr -- n ) ( n adr -- ) | Read long at COG memory adr Write long to COG memory adr Read long at LUT memory adr Write long to LUT memory adr | TAQOZ# 0 COG@ .LONG FDA0.0990 ok TAQOZ# 0 LUT@ .LONG 0000.04D2 ok TAQOZ# $CAFEBABE 2 COG! 2 COG@ .LONG CAFE.BABE ok |
C++ C-- W++ W-- ++ -- | ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) | Increment byte at adr hub memory Decrement byte at adr hub memory Increment word at adr hub memory Decrement word at adr hub memory Increment long at adr hub memory Decrement long at adr hub memory | |
C~ C~~ W~ W~~ ~ ~~ | ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) ( adr -- ) | Byte set 0 at hub memory adr Byte set $FF at hub memory adr Word set 0 at hub memory adr Word set $FF at hub memory adr Long set 0 at hub memory adr Long set $FF at hub memory adr | |
DATA? | ( addr cnt -- flag ) | Test for non-zero data starting at memory addr for cnt longs | |
BIT! SET CLR SET? |
SERIAL FLASH
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
BACKUP | ( -- ) | BACKUP the TAQOZ system with any added user words into the last 64KB of 1MB of Flash | |
RESTORE | ( -- ) | Restore the backup after reset | |
.SF | ( -- ) | Displays flash memory manufacturers’ I.D. serial number etc. Useful for checking communication with flash is working | TAQOZ# .SF $EF70_1800 $0F0B_2826 $E468_5CF4 ok |
SF | |||
SF? | |||
SF@ | ( adr -- n ) | Returns long from flash address adr | |
SFC@ | ( adr -- c ) | Returns byte from flash address adr | |
SFW@ | ( adr -- w ) | Returns word from flash address adr | |
SFER4 SFER32 SFER64 | ( adr -- ) | ||
SFERASE | ( -- ) | ||
SFINS | |||
SFPINS | ( -- n ) | Returns constant n = $3d3a3b3c , the four pin numbers of the Flash memory interface | |
SFJID | ( -- n ) | Read serial Flash Jedec ID | |
SFRDS | |||
SFSID | Read serial Flash serial number | ||
SFWD | |||
SFWE | |||
SFWRPG | ( src dst -- ) | ||
SFWRS | (hubsrc sfdst cnt -- ) |
SD CARD (FAT32)
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
@BOOT | ( sector str -- ) | ||
@FAT | ( fat# -- sector ) | ||
@ROOT | |||
!SD | ( -- ocr | false ) | Initialise the SD card in SPI mode and return with the OCR, else false if an error occurs | |
!SX | |||
ACMD | ( data acmd -- res ) | ||
cid | |||
CMD | ( data cmd -- res ) | ||
DIR | ( -- ) | lists info about the card, FAT32 and directory entries | |
fat | ( -- n ) | Constant n = $FF00 the start location of the file allocation table | |
FGET | |||
FLOAD | loads a TAQOZ source file | ||
FLUSH | ( force -- ) | ||
FOPEN | ( addr -- ) | Opens file by address visible in listing from DIR | TAQOZ# $A11E00 FOPEN --- ok TAQOZ# 0 100 SD DUMP --- 00000: 66 6C 66 32 61 24 20 36 20 34 20 36 20 2D 31 20 'flf2a$ 6 4 6 -1 ' 00010: 34 0A 33 78 35 20 66 6F 6E 74 20 62 79 20 52 69 '4.3x5 font by Ri' 00020: 63 68 61 72 64 20 4B 69 72 6B 20 28 72 61 6B 40 'chard Kirk (rak@' 00030: 63 72 6F 73 66 69 65 6C 64 2E 63 6F 2E 75 6B 29 'crosfield.co.uk)' 00040: 2E 0A 50 6F 72 74 65 64 20 74 6F 20 66 69 67 6C '..Ported to figl' 00050: 65 74 2C 20 61 6E 64 20 73 6C 69 67 68 74 6C 79 'et, and slightly' 00060: 20 63 68 61 6E 67 65 64 20 28 77 69 74 68 6F 75 ' changed (withou' ok |
FREAD | ( sdsrc hubdst bytes -- ) | ||
FWRITE | ( hubsrc sddst bytes -- ) | ||
MOUNT | ( -- ) | Mounts SD card and reports type, s/n, formatted capacity | TAQOZ# MOUNT .SDSC64G 6269_8201 P2 CARD 32k 60,890M ok |
SD | |||
SDBUF | |||
sdpins | ( -- n ) | Returns constant n = $3c3a3b3d , the four pin numbers of the SD card interface | |
SD@ SD! | ( xaddr -- long ) (long xaddr -- ) | ||
SDC@ | ( xaddr -- byte ) | ||
SDW@ | ( xaddr -- word ) | ||
SD? | ( -- flag ) | ||
SDADR | ( xaddr -- addr ) | ||
SDRD | ( sector dst -- ) | ||
SDRDS | ( sector dst cnt -- crc | false ) | ||
SDWR | ( src sect -- flag ) | Write from src to xdst in the SD | |
SDWRS | ( hubsrc sdadr cnt -- ) | ||
SECTOR | ( sect -- sdbuf ) |
DEFAULT RADIX WORDS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
HEX DECIMAL BINARY | ( -- ) | Switch the number base to hexadecimal Switch the number base to decimal Switch the number base to binary | TAQOZ# 23 DUP . 23 ok TAQOZ# HEX ok TAQOZ$ . AC ok |
Note: The TAQOZ prompt suffix changes to show the default radix. $=hex #=decimal %=binary
NUMBER I/O
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
. U. | ( n -- ) ( n -- ) ( u -- ) | Display a signed number n Display a signed number n Display unsigned number u | |
<# # #S HOLD #> | ( c -- ) ( -- str ) | Start a new formatted number string Insert the next digit of the number being displayed into the formatted number string Insert all remaining significant digits of the number Insert the character on the stack into the formatted string Terminate the formatted number leaving the address of the formatted number string at tos, ready for display | TAQOZ# pub TODOLLARS ( d -- ) TAQOZ# <# # # 46 HOLD #S 36 HOLD #> ; ok TAQOZ# 1234. TODOLLARS PRINT$ $12.34 ok |
.ADDR | ( n -- ) | At column zero of the display, print n as 5 digit hex followed by : and <space>, as used in memory dumps | TAQOZ# HEX 1FF .ADDR TAQOZ# 001FF: ok |
.AS | |||
.AS” | ( n -- ) | Display n with format as per string spec: # Convert one digit (default is decimal) ~ Toggle leading zero suppression \ pad leading zeros with spaces $| Hexadecimal *| Convert all remaining digits 4| Convert 4 digits Terminated with “ | TAQOZ# 32767 .AS” $|####“ $EFFF ok |
.B .BYTE | ( n -- ) | Display n as two digit hex byte 00-FF | TAQOZ# 253 .B DF ok |
.BIN | ( n -- ) | Display n in binary with leading % | TAQOZ# 33 .BIN %100001 ok |
.DEC | ( n -- ) | Display n in decimal with at least a single digit | |
.DECL | ( n -- ) | Display n as decimal "##,###,###,####" | |
.DEC4 | ( n -- ) | Display n as decimal "####" ( n -- ) | |
.H | ( n -- ) | Display n as hex nibble 0-F | |
.L .LONG | ( n -- ) | Display n as eight digit hex 00000000 - FFFFFFFF | |
.W .WORD | ( n -- ) | Display n as four digit hex 0000-FFFF |
CHARACTER I/O
WORDS | Stack | DESCRIPTION | CONSOLE EXAMPLES | |
." | ( -- ) | Print text in input stream until terminating “ | ||
CONEMIT EMIT CLS CRLF CR SPACE SPACES EMITS | Output to the terminal | |||
CONKEY KEY WKEY | ( -- c | false ) | Return with the next character in the console buffer or else a flag = false ( 0 ) | ||
CTYPE | ( str cnt -- ) | Display cnt chars, starting at address str | ||
KEY! | ( c -- ) | Force a character as the next key read in the input stream | ||
CON | ||||
PRINT$ | ( str -- ) | Display string at address str | TAQOZ# 16 BYTES mystring ok TAQOZ# “ FRED” mystring $! ok TAQOZ# mystring PRINT$ FRED ok | |
<D> | ||||
SPIN | Overlay one of four characters | / - \ in turn on the terminal as an ‘in progress’ indicator ( -- ) Requires a terminal emulator that can respond to the ‘backspace’ char | TAQOZ# pub HANGON ( -- ) --- Show user something’s in progress TAQOZ# 100 0 DO TAQOZ# SPIN 500 ms TAQOZ# LOOP ; Note: In practice, there would be other code within the loop, checking for some change in state | ||
TERM | Main terminal console |
I/O PORTS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
HIGH LOW FLOAT | ( pin -- ) | Set the direction and output of a pin | TAQOZ# 4 HIGH 4 PIN@ . -1 ok TAQOZ# 4 LOW 4 PIN@ . 0 ok |
H L F R T | ( -- ) ( -- ) ( -- ) ( -- flag ) ( -- ) | Set a preselected pin to a high output Set a preselected pin to a low output Set a preselected pin to a floating state(high impedance) Set a preselected pin to input, flag = input state | TAQOZ# 4 PIN ok TAQOZ# L R . 0 ok TAQOZ# H R . -1 ok |
SMARTPINS
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
WRPIN WXPIN WYPIN RDPIN RQPIN AKPIN | ( dst -- ) ( dst -- ) ( dst -- ) ( -- res ) ( -- res ) ( -- ) | Same as assembler - write dst to mode register of smart pin S[5:0], acknowledge smart pin. Write dst to parameter "X" of smart pin S[5:0], acknowledge smart pin. Set smart pin S/# parameter Y to dst res = smart pin S/#, ack Res = smart pin S/#, don't ack /#, acknowledge smart pin | |
COM NONE | direct terminal output to a smartpin (after init) | ||
WRACK | ( n -- ) | Write smartpin data, wait for empty then acknowledge | |
WAITPIN TXDAT DUTY NCO | ( buf cnt -- ) ( val -- ) ( n -- ) | Write buffer direct to WYPIN Set Numerically Controlled Oscillator duty cycle Start Numerically Controlled Oscillator, n sets relationship to system clock - see P2 manual | TAQOZ# 48 PIN $8000_0000 NCO ok TAQOZ# --- pin 48 emits ½ system clock ok |
HILO | ( high low -- ) | Specify High and Low times for subsequent PULSE or PULSES words | |
HZ KHZ MHZ BLINK | ( freq -- ) ( -- ) | Set PIN to NCO mode and output the frequency BLINK sets an output to 2 Hz - for LED flashing | TAQOZ# 4 PIN 10 MHZ ok TAQOZ# 5 PIN BLINK ok |
MUTE | ( -- ) | Cancel the smartpin mode. Stops any frequency, sound, or other function from a pin | |
PIN @PIN | ( pin -- ) ( -- pin ) | Select a smartpin to use, this setting is remembered until another pin is selected Read setting back | TAQOZ# 5 PIN @PIN . 5 ok |
PULSE | |||
PULSES | ( cnt -- ) | Generate cnt pulses according to specified high and low times | |
PW | ( width -- ) | ||
PWM SAW | ( duty frame div -- ) | Set pin to PWM triangle mode using duty, frame, and divider parameters. Use SAW instead for sawtooth. | TAQOZ# 4 PIN 1000 4000 1 PWM ok |
SEROUT | |||
SETDACS | ( n -- ) | DAC3 set to bits 31-24, DAC2 set to bits 23-16, DAC1 set to bits 15-8, DAC0 set to bits 7-0 of n | |
BAUD | ( baud mode -- ) | ||
TXD | ( mode baud -- ) | Setup a pin as an asynchronous transmit using the supplied parameter as a baud rate | TAQOZ# 34 PIN 8 BIT 40 M TXD @NAMES $10 TXDAT ok |
RXD | ( mode baud -- ) | Setup a pin as an asynchronous receive using the supplied parameter as a baud rate | |
BIT | ( n -- ) | Specify the data bits (1..32) to be used whenever a pin is switched to RXD or TXD modes. If this is not specified it will default to 8 | TAQOZ# 8 BIT 115200 TXD ok |
SPI
Serial Peripheral Interface is implemented as cog instructions that handle half-duplex read or write in increments of 8-bits. Once the SPI pins have been set then any read or write will automatically enable the Chip Enable and only requires a SPICE to turn it back off if required. Sometimes all that is required here is to SPICE just before starting a new command to reset the slave.
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
SPIWB SPIWW SPIWC | ( n1 -- n1 ) | Write Byte or Word or SD Command to SPI pins. 200ns/div example sending $A5 then SPICE | TAQOZ# $12345678 SPIWB .S DATA STACK x1 1 $0000.0078 120 ok |
SPIWL | |||
SPIWM | |||
SPIRD | ( n1 -- n2 ) | Read 8-bits left into n1 so that n2 = n1<<8+new. Four successive SPIRDs will receive 32-bits | |
SPIRDL | Read a long from SPI | ||
SPICE | Release SPI chip enable line | ||
SPIPINS | ( &cs.mi.mo.ck -- ) | Setup SPI pins as & ce.miso.mosi.sck | TAQOZ# &32.33.34.35 SPIPINS ok |
SPIRX SPITX SPITXE | ( adr cnt -- ) | Send or receive between memory and SPI 500ns/div example writing 4 bytes from memory | TAQOZ# &32.33.34.35 SPIPINS ok TAQOZ# 0 4 SPITXE SPICE ok |
COG/HUB
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
COGID COGINIT COGSTOP COGATN | ( -- cog# ) (adr cog# -- ) ( cog# -- ) (maskw -- ) | Return current cog# Same as COGINIT in PASM - also saves information in cog TASK block at adr Stop cog# Strobe the “attention” event flag for all cogs whose corresponding bits are high in maskw ( bit 0 - 8 for P2 ) | |
NEWCOG | ( n -- ) | An idling instance of TAQOZ is loaded and run in cog#n | |
POLLATN SETEDG POLLEDG | ( -- flag ) ( edge pin -- ) ( -- flag ) | flag = ATN attention event flag, ATN event flag then cleared | |
HUBSET WP WE REBOOT | ( n -- ) | HUBSET - configure various global circuits Write Protect ROM Write Enable ROM | |
TASK | ( cog# -- adr ) | Index the cog's task variable and return with its address | |
WAITCNT | ( -- ) | Continue from last count (must be called before target is reached) | |
WAITX | ( delta -- ) | Calculate and set the cnt delta and waitcnt |
COMMENTS
WORDS | DESCRIPTION | CONSOLE EXAMPLES |
{ <multiline comment the compiler ignores> } | Ignores all text up to the matching curly brace } | |
--- <rest of line ignored> \ <rest of line ignored> // <rest of line ignored> | Ignores the rest of the line Ignores the rest of the line, do not echo Ignores the rest of the line | |
( <words to ignore> ) | Ignores characters until closing brace ) Use on one line only, else use { } multiline |
WORDS | STACK | DESCRIPTION | CONSOLE EXAMPLES |
IC@ | ( -- c ) | ||
ERROR | count errors and force a new line to display error | ||
char | |||
[C] | force compilation of the next word | ||
[W] | append this wordcode to next free code location + append EXIT (without counting) | ||
ASM | |||
VAR | |||
AUTO | autorun | ||
WSLED | something to do with driving an intelligent LED WS2812 | ||
DATCON | |||
GRAB | IMMEDIATE --- executes preceding code to make it available for any immediate words following | ||
CLKDIV RCSLOW |
If anyone is interested, this is the way I would do it in TAQOZ in one line of code and hit enter.
48 PIN 12000 6000 HILO 50 LOW 16 PULSES WAITPIN 3 PULSES 50 HIGH
My clock is 300MHz so 40us= 12000 and I specify P48 as the target smartpin.
HILO takes high and low count and sets up the Smartpin.
LOW drives P50 low.
PULSES writes wypin with that value (just an alias)
WAITPIN waits for smartpin ack
1Mbd hello world
20Mbd hello world
20Mbd hello world using txdat
TAQOZ# 52 pin 825 mV --- ok
Meter reading