Working with Variables, Constants and Arrays

Active usage of nnCron often requires to store various data for long periods of time. Variables, constants and arrays can be used for this purpose. Beside allowing to use all the tools for handling variables, constants and arrays provided by Forth programing language (SP-Forth), nnCron also privides some convenient tools of its own.



Variables

Variables inteneded for storing numerical values are created (declared) with word VARIABLE:

VARIABLE <var_name>

Variable should be declared in the beginning of a task, before the Action: section; operations of assigning/fetching values to and from variables should be performed inside of Action: section. When a variable is created, it is assigned a value of 0.

Example:

#( test_variable
NoActive
VARIABLE my_var Time: * * * * * * Action: \ ... working with value of my_var )#

In order to assign a value to a variable, use word ! (exclamation point) :

<var_value> <var_name> !

Example:

\ now variable my_var contains  a numerical value of 12
12 my_var !

To get (retreive) a value from a variable, use word @:

<var_name> @

Example:

\ adding 10 to the value of  my_var (12)
\ the sum is 22
my_var @ 10 +

Example:

#( variable_task
NoActive   \ we are going to start this task manualy
\ creating two variables
VARIABLE first_var
VARIABLE second_var
Action:
    \ assigning value of 18 to first_var
    18 first_var !
    \ adding  4 to the value of first_var
    \ and storing the result in second_var 
    \ (4 + 18 = 22)
    4 first_var @ + second_var !
    \ diaplaying the value of second_var
    MSG: "second_var = %second_var @%"
)#

There are also special words ON and OFF which facilitate use of variables with IF ... ELSE ... THEN construct . Word ON sets the value of a variable to -1, and OFF to 0:

\ setting first_var to -1
first_var ON
\ setting first_var to 0
first_var OFF

Example:

#( test_variable2
NoActive
\ creating a variable is_it_true?
VARIABLE is_it_true?
Action:
    \ assigning a value of -1 to is_it_true?
    is_it_true? ON
    \ fetching the value of this variable 
    \ checking it with a conditional construct
    is_it_true? @
    IF 
        MSG: "TRUE!"
    ELSE 
        MSG: "FALSE!"
    THEN
    \ assigning a value of 0 to is_it_true?
    is_it_true? OFF
    \ fetching the value of this variable 
    \ checking it with a conditional construct
    is_it_true? @
    IF 
        MSG: "TRUE!"
    ELSE 
        MSG: "FALSE!"
    THEN
)#

Some of words used in Forth and in nnCron return so called double numbers. A variable, that can hold the double number is declared using the word 2VARIABLE. To assign a value to this variable use 2!. To fetch a value use 2@.

Examples:

#( test_file_size1
NoActive
\ creating a variable fsize which can hold double numbers
2VARIABLE fsize
Action:
    \ saving the mkisofs.exe file size to fsize
    FILE-SIZE: "c:\temp\mkisofs.exe" fsize 2!
    \ displaying the fsize contents in a message
    MSG: "mkisofs.exe size = %fsize 2@ <# #S #>%"
)#

#( test_file_size2
NoActive
\ creating a variable fsize2 which can hold double numbers
2VARIABLE fsize2
Action:
    \ assigning a double number 10240 to fsize2
    10240. fsize2 2! 
    \ comparing the fsize2 contents with mkisofs.exe file size
    fsize2 2@ FILE-SIZE: "c:\temp\mkisofs.exe" D<
    IF
        MSG: "<"
    ELSE
        MSG: ">"
    THEN
)#

See also Variables, Constants and Arrays Visibility Scope


Self-fetching Variables (VALUE)

Another way to create a somewhat different type of numerical variables (so-called self-fetching variables) is to use word VALUE:

<initial_value> VALUE <var_name>

As you can see, a self-fetching variable is assigned some value at the moment when it is created:

10  VALUE first_var
100  VALUE second_var

One of the convenient features of self-fetching variables is that you can fetch their values without using a special word. All that you need to do in order to place a value of a self-fetching variable on stack is just to use its name in a program:

#( test_value_var
NoActive
\ declaring a variable bingo and setting its value to 21 21 VALUE bingo Action: \ displaying its value
MSG: "You are a winnner - %bingo%!" )#

To assign a value to a self-fetching variable, use word TO. Here is and example of its use:

<new_var_value> TO <var_name>

Example:

#( test_value_var1
NoActive
\ declaring a variable counter_var and setting its value to 0
0 VALUE counter_var
Action:
    \ displaying its value
    MSG: "Initial value = %counter_var%"
    \ assigning a new value to it ( 5)
    5 TO counter_var
    \ displaying the new value of our variable
MSG: "New value = %counter_var%" )#

See also Variables, Constants and Arrays Visibility Scope


Constants

Sometimes it is necessary to create a "variable" whose numerical value should not change later. Such a "variable" is called a constant.

To create a constant, use a special word CONSTANT:

<const_value> CONSTANT <const_name>

All that you need to do in order to place a value of a constant on stack is just to use its name in a program:

#( test_constant
NoActive
\ creating a constant one_o_one and setting its value to 101 101 CONSTANT one_o_one Action: \ displaying the value of our constant MSG: "One-O-One = %one_o_one%" \ using the constant in an arithmetical operation MSG: "One-O-One - 1 = %one_o_one 1 -%" \ displaing the constant again, \ to make sure it has not changed MSG: "One-O-One = %one_o_one%" )#

See also Variables, Constants and Arrays Visibility Scope


Arrays

Array is a special type of structure that allows to store multiple values under a single name. One of the traditional uses for arrays in nnCron is to temporarily store strings in them. Methods of handling strings are described in detail in "Forth First Aid".

In order to create an array, use word CREATE:

CREATE <array_name>

To allocate a required memory space for the new massive, use word ALLOT.

Example:

#( test_array
NoActive
\ creating an array test_arr 256 byte in size CREATE test_arr 256 ALLOT
Action:
\ placing a string to our array
S" this is our string" test_arr PLACE
\ reading a string from the array \ and displaying it
MSG: "Array contents: %test_arr COUNT%"
)#

Of course, arrays can be used to store any kind of values, not just strings. It is even possible to initialize arrays right at the monent of their creation, e.g.:

\ ˝reating an array containing five integers
CREATE numbers 1 , 2 , 3 , 4 , 5 ,

It is not a purpose of nnCron documentation to provide a more detailed desription of using arrays. For additional information, consult documentation of Forth programing language.

See also Variables, Constants and Arrays Visibility Scope


File and Registry Variables

The main drawback of variables, constants and arrays discussed above is that they store their values in computer memory, and, therefore, these values will be lost after the computer is turned of or after nnCron is unloaded from the memory. But what if we want to save a value of a variable, constant or array during the period when computer is off or nnCron is not running?

Just for this purpose nnCron provides a number of words for creating string variables which are not affected by restart of a computer or of nnCron. These are so-called file variables and registry varibiables, i. e. variables which store their values not in computer memory, but in special files and registry keys.

fVAR <var_name> (file variable)
regVAR <var_name> (registry variable)

When file/registry variable is created, it is initialized with an empty string. To store your string in such a variable, use word TO. To access the current value of a variable, just use its name in a program:

#( test_persistent_var
NoActive 
\ creating a file variable and a registry variable
fVAR file_var
regVAR reg_var
Action:
    \ assigning string values to variables
    S" first string" TO file_var
    S" second string" TO reg_var
    \ displaying both variables on screen
    MSG: "There are %file_var% and %reg_var%"
)#

As told above, file and registry variables are used to store strings, but if you want to use them to store numbers rather then strings, you can use special words which convert a number to a string and vice versa (coverting numbers to strings and strings to numbers is discussed in detail in "Forth First Aid"):

\ converting a number to a string and storing it in a file variable
N>S TO file_var
\ reading a string from a file variable and converting it to string file_var S>NUM

If we want to get a complete idea about using file and registry variables, we have yet to talk about out where they are stored. Each file variable is stored in a file with the same name; this files are located in subdirectory var of the nnCron installed directory. Registry variables store their values in registry keys which have the same name as variables and are located in a registry branch HKEY_LOCAL_MACHINE\SOFTWARE\nnSoft\var\.

If it is necessary to change storage locations of file and registry variables, there are special words which can be used for that purpose:

FileVarPath! ( a u -- )
RegVarPath! ( a u -- )

These words specify the path to directory or registry branch where file/registry variables should be stored. Don't forget a backslash (\) at the end of the specified path.

S" c:\temp\var\" FileVarPath!
S" HKEY_LOCAL_MACHINE\SOFTWARE\var\" RegVarPath!

Please note that these words are intended for use in nncron.ini. They cannot be used in tasks.

uFileVarPath! ( a u -- )
uRegVarPath! ( a u -- )

These words specify the path to directory or registry branch where file/registry variables should be stored. Don't forget a backslash (\) at the end of the specified path.

S" c:\temp\var\" uFileVarPath!
S" HKEY_LOCAL_MACHINE\SOFTWARE\var\" uRegVarPath!

These words are intended for use in tasks.

If required, you can delete unnecessary file or register variables with words FILE-DELETE:/REG-DELETE-KEY:.

See also Variables, Constants and Arrays Visibility Scope


Visibility Scope of Variables, Constants and Arrays

All variables (VARIABLE, VALUE), constants (CONSTANT) and arrays(CREATE ... ALLOT) are global, they can be seen from other tasks and are shared by all instances of any given task. Therefore, in order to avoid unnecessary confusion, one should take a great care to ensure that all variable names are unique.

Here are some details for most curious ones: although theoretically all variables are global, each unique task will work with its own variable. Constructs of %VAR-NAME @% type, however, will always return the value of variable which was used last.

Example:

#( t1
VARIABLE V1
Action:
    1 V1 !
    MSG: "%V1 @%"
)#

#( t2
VARIABLE V1
Action:
    2 V1 !
    MSG: "%V1 @%"
)#

Now, a question: what message will contain the message box displayed by the first task?

It depends on whether or not the second one has been executed. If it has, the first one will output 2, otherwise, it will output 0 (the initial value of all variables).

It is possible to create arrays and variable which will be unique for each instance of a task (there will be as many variables as there are instances of task running). In order to achieve this result, create variables with these words:

USER var1
USER-CREATE var2 256 USER-ALLOT
USER-VALUE var3

You can also allocate memory using word ALLOCATE. In this case each task will also have its own buffer:

#( vars
NoActive
USER buf
Action:
    256 ALLOCATE THROW buf !
    ACTIVE-WINDOW buf @ PLACE
    5000 PAUSE
    MSG: "%buf @ COUNT%"
)#