Sunday, May 19, 2013

More Thoughts on Txtzyme - and a musical interlude

In the last post I discussed the simple interpreted language Txtzyme and offered some ideas on how the language could be extended.

Ward Cunningham, the creator of txtzyme describes it as text that "catalyses action".  I think this is a fair description.

In summary, Txtzyme uses some very simple methods to produce an executable program and interface with the Arduino hardware. It uses the following Arduino functions to enable the interpreter to implement a simple serial interface and access the digital and analogue I/O:

Serial.read
Serial.print
digitalWrite
digitalRead
analogRead
delay
delayMicroseconds

Much of the simplicity is that a single ASCII character is interpreted to produce an action, and by stringing ASCII characters together the interpreter will handle them in turn and execute each action in order.

The interpreter allows the use of a simple loop structure, by repeating the actions enclosed within braces (curly brackets)  {.......}

Numerical characters are decoded into a decimal number and assigned to a single numerical variable   x which is used as a parameter to control some of the actions.  For example, time is handled using the two native Arduino functions delay (mS) and delayMicroseconds.

These are given the characters m and u respectively, such that

1000m  is a delay of 1000 milliseconds
500u   is a delay of 500 microseconds

These are useful to slow down program operation, such as when reading ports or ADCs and printing of for creating accurate timing loops for generating frequencies.

Extending the Ideas

My first foray into txtzyme was to enhance the simple interpreter such that it could execute a series of user routines invoked by typing their uppercase ASCII character name. The use of uppercase differentiates them from what I will call txtzyme primitive functions which are represented by lowercase characters.

I fitted a small loudspeaker to digital 6 and from it I was able to produce a series of beeps and tones, however it soon got tedious having to retype the txtzyme strings into the input buffer each time I wanted to try a new tone. This inspired me onto the next level of extension,  the means to code each txtzyme string into a named array in memory, and execute the string just by typing its name. This is an idea heavily borrowed from the idea of Forth words, but simplified to use just a single uppercase character (A-Z) as the name.

For the demonstration, I defined six user routines called A-F, each of which caused a short musical tone to be output from pin 6 of the Arduino.  The code to do this is on my github gist

https://gist.github.com/anonymous/5604829

Each uppercase character defines a txtzyme array of characters which can be interpreted as though it was typed into the serial terminal buffer.  For example, a musical note is defined by the following characters stored in the A array and will be invoked whenever the text interpreter encounters the character A.  6d selects digital port pin 6, and produces 75 cycles of tone.  The port pin is low for 708 microseconds and then high for 708 microseconds.

char A[64] = "6d75{1o708u0o708u}";

The A array has a total of 64 characters reserved for it although only 17 are used. This is not very efficient on RAM usage, but since there are only 26 user routines, less than 1700 bytes of RAM are    allocated for this storage.

I then proceeded to write similar strings for the "notes"  B through to F. Once compiled, these notes could be played just by typing ABC, or any other sequence into the serial terminal.

I then defined G by the array

char G[64] = "ABCDEFFEDCBA";

To my delight, and surprise it played the sequence of 12 notes perfectly.  Using musical tones to debug program flow is a neat trick, and a lot easier and quicker than counting flashes on a LED!

Another though was what happens if a word calls itself - using this example

char H[64] = "GH";

Typing H produced an infinite loop of tone sequence - not unsurprisingly.

Finally I tried an example that reads and prints the adc0 and plays a tune each time

char I[64] = "{0spABC}";

Notice how this is just a loop structure with no controlling parameter. This means that the number of times the loop is executed can be specified by typing that number before the I,

5I    - perform the I sequence 5 times.

Colon Definitions

The Colon Definition is a construct borrowed unashamedly from Forth.  It is the method by which new words (actions) can be defined and stored into memory. This has the advantage of not having to keep typing new stings repeatedly just to try them out, but also provides a simple means to write new code, test it and edit it until it works.

The above demonstration bypassed the need for the colon definition, by using a hard coded array of characters defined in the C code. Now it is time to bite the bullet and extend the interpreter to handle the colon definition.

The example above written as a colon definition would be written

:A6d75{1o708u0o708u};

As txtzyme ignores whitespace, this could be rewritten with spaces to make it more legible

: A 6d 75{1o 708u 0o 708u};

To handle the colon definition we need a routine that on seeing the colon, gets the next character (capital A) as the name and copies the next n characters to an array called by that name. This process ends when the interpreter encounters the semicolon.

Unfortunately, I'm not really a C programmer, so the clever use of pointers and the reference and dereference operators tends to fox me somewhat, but after an hour of some kludgy coding, I had a function that would perform the colon definition task, and assemble the definition into a named array.

In theory the name of the definition eg A, should just be an address in memory, and on typing A, the interpreter starts processing the characters that appear at that address until the newline character is encountered.

Viewing and Editing txtzyme definitions.

I also decided that the ? character would be a good way of examining the code contained within a definition, allowing cut and paste editing from the serial terminal.

So  ?A prints out the entire definition of A to the terminal

:A6d75{1o708u0o708u};


To be continued.





No comments: