Tiger BASIC - Reference

  1. Alphabetical list of keywords
  2. Functional list of keywords
    1. Comments
    2. Assingments and definitions
    3. Conditionals
    4. Unconditional jumps
    5. Loop constructs
    6. Operators
    7. Special functions
    8. Display commands
    9. Communication and parsing commands
  3. Syntax
    1. Expressions
    2. Conditions
    3. Comments and command separators
    4. Assignments and definitions
    5. Conditionals
    6. Unconditional jumps
    7. Loop contructs
    8. Special functions
    9. Display commands
    10. Communication and parsing commands


Bottom
  1. Alphabetical list of keywords

    All keywords can be completely upper or lower case, i.e. 'AND' or 'and'.
    abs()
    addr()
    and
    append
    arccos
    arcsin
    arctan
    asc()
    bit
    bitreg
    case
    chr()
    cos
    cosh
    const
    default
    dim
    edit
    edit_numeric
    edit_text
    else
    elsif
    end
    endif
    endsel
    exit_edit
    float()
    for
    force_log
    gosub
    goto
    hex()
    if
    include
    integer()
    let
    ln
    log
    mem
    modbus_read()
    modbus_write()
    next
    or
    print
    reg
    rem
    repeat
    return
    scale()
    select
    serial_input
    serial_pointer
    set
    sin
    sinh
    sqr
    step
    tan
    tanh
    then
    to
    until
    write
    xor
    Top
  2. Functional list of keywords

    1. Comments and command separator

      //
      rem
      :
      \
      Top
    2. Assignments and definitions

      =
      bit
      bitreg
      const
      dim
      let
      mem
      reg
      set
      Top
    3. Conditionals

      if..then ... elsif ... else ... endif
      select ... case ... default ... endsel
      Top
    4. Unconditional jumps

      end
      gosub
      goto
      return
      Top
    5. Loop constructs

      repeat ... until
      for..to..step ... next
      Top
    6. Operators

      Basic arithmetical operators

      +
      -
      *
      /
      ^
      ()

      Higher math operators

      arccos
      arcsin
      arctan
      cos
      cosh
      ln
      log
      sin
      sinh
      sqr
      tan
      tanh

      Comparison operators

      <
      <=
      =
      >=
      >
      <>
      and
      or

      Logical bit operators

      and
      or
      xor
      Top
    7. Special functions

      abs()
      addr()
      asc()
      chr()
      force_log
      include
      scale()
      Top
    8. Display commands

      append
      edit
      edit_numeric
      edit_text
      exit_edit
      write
      Top
    9. Communication and parsing commands

      float()
      hex()
      integer()
      modbus_read()
      modbus_write()
      print
      serial_input
      serial_pointer

    Top
  3. Syntax

    1. Expressions

      Expressions represent a specific value and are used for assignments and in conditions. Simple expressions consist of only a number or a variable. Complex expressions are constructed by using arithmetic, logical or higher math operators.
      Meters of the Tiger series can handle up to 10 levels of nested expressions.
      Example:
      #A = &CH1 + arcsin ( &CH2 * ln &CH3 )

      Expressions using basic arithmetic or logical bit operators

      +, -, *, /, ^, ()
      and, or, xor

      Syntax
      exp ::= "-" exp
      exp ::= "(" exp ")"
      exp ::= exp op exp

      op ::= "+" | "-" | "*" | "/" | "^" | and | or | xor
      Example:
      #A = &CH1 ^ -( &Ch2 / 10 )
      #B = #A and 0x07 // bit 0-2 of #A

      Please note that AND and OR coincide with the operators to combine conditions. Therefore, logical bit operations have to be put inside parentheses in conditions

      Expressions using higher math operators

      arccos, arcsin, arctan, cos, cosh, ln (base e), log (base 10), sin, sinh, sqr, tan, tanh

      Syntax
      exp ::= unop exp

      unop ::= arccos | arcsin | arctan | cos | cosh | ln | log | sin | sinh | sqr | tan | tanh
      Example:
      #A = sqr &CH1

      Top
    2. Conditions

      Conditions are used to compare the value of a variable or a register to an expression. They occur in the IF..THEN construct and the REPEAT..UNTIL loop.

      <, <=, =, >=, >, <>
      on, off, true, false
      and, or

      Syntax
      condition ::= exp comp exp
      condition ::= bit comp bit
      condition ::= "(" condition ")"
      condition ::= condition { comb_op condition }

      comp ::= "<" | <= | "=" | >= | ">" | <>

      bit ::= bitreg | bitvalue
      bitvalue ::= on | off | true | false
      comb_op ::= and | or
      Example:
      if #A >= &CH1 - #offset then
        write " too high "
      endif

      if |CAPTURE_PIN = off and |RECEIVE_READY = true then
        gosub read_serial_data
      endif

      Please note that AND and OR coincide with the logical bit operators. Therefore, logical bit operations have to be put inside parentheses in conditions:

      if (&DISPLAY and 0x00000001) = 0 then
        // display value is even
      endif

      Top
    3. Comments and command separator

      :, \

      Tiger BASIC has no line number but expects each command to be on a single, separate line.
      To place more than one command in one line you can use the colon as a separator.
      To write a single command in more then one line you can use the backslash at the end of the line.
      Example:
      dim A[] = [ "Hello ", \
                  "World" ]
      write A[0] : append A[1]

      //, rem

      Comments are started either by '//' or by REM and continue up to the end of the line. Whereas '//' can be placed everywhere, REM works like a command. Therefore it has to be separated with a colon from any preceding commands.
      Example:
      // if the line contains only the comment
      rem the comment markers don't differ
      write "Hello " // but when preceded with other commands
      append "World" : rem REM needs a separator

      Top
    4. Assignments and definitions

      The following commands allow you to define names for variables, registers, and constants. These names have to be unique and may only contain letters, numbers and underscores, however they cannot start with a number. Please note that all user defined names as well as the pre-defined register names are case sensitive (i.e. TIMEOUT is not the same as TimeOut).

      =, let, set

      All variables are global and have to be assigned an initial value before use (i.e. regarding to their first occurence in the source code). Variables are only available for numeric values. The variable type (integer, float) is indicated by a prefix (#, %).

      There are also a great number of pre-defined variables which refer to registers or bit registers and are prefixed with '&' or '|', respectively. These do not have to be assigned before their first use.

      To assign a variable, you can simply use the '=' operator or you can explicitly use the LET command for registers or the SET command for bit registers.
      Example:
      #A = -100
      let %FloatValue_1 = 100.0
      &CH1 = #A

      mem

      With the MEM command registers can be preset when the macro is downloaded. This is typically used for meter setup and to initialize USER_MEMORY registers. MEM can also be used to initialize register arrays.
      Example:
      mem &USER_MEMORY_1 = 0

      // preset setpoint 1-3
      mem &SETPOINT1[] = [ 1000, 2000, 3000 ]

      bit, reg

      To improve code readability, users can define other names for the predefined registers using the REG or BIT command. The name is prefixed with '&' or '|' respectively.
      Example:
      reg &WaterTemperature = &CH1
      bit |Phase_1 = |LED1

      bitreg

      The BITREG command allows the user to address single bits of a register by name. The bits are named starting with bit 0 and have a '|' as prefix. Not all bits have to be named.
      Example:
      bitreg &CODE2 = [ ,,,,,, |FREQ_50HZ, |FAST_MODE ]

      const

      Number values can be named with the CONST command to improve code readability. It also allows to set a value that is used multiple times throughout the code in a single spot. Constants have no prefix and are replaced at compile time with their values (so the compiled code does not change whether you use constants or not).
      Example:
      const ButtonTimeout = 40
      const DefaultScaleFactor = 0.125

      There are a few pre-defined constants for the CHR() command as well as for modbus communications.

      dim

      String or integer arrays can be defined with DIM, outside of any macro at the top of the program. The first element is refered to with index 0. Integer arrays can contain only 16bit unsigned values.
      Example:
      dim A[] = [ "0 Butane", "1 Oxygen", "2 Hydrogen", "3 Nitrogen" ]
      dim B[] = [ 100, 101, 104, 109, 116, 125, 136, 149, 164, 181, 200 ]
      ...
      write A[0] // this is equivalent to: write "0 Butane"

      Top
    5. Conditionals

      The following two statements allow you to execute a sequence of commands when a certain condition is met.

      if..then ... endif
      if..then ... else ... endif
      if..then ... elsif ... else ... endif

      Syntax
      if_statement ::= if condition then
                         statement_sequence
      { elsif condition then statement_sequence }
      [ else statement_sequence ]
      endif
      The IF statement can have any number of ELSIF branches but only one ELSE. Please note that the ENDIF command is mandatory.
      Example:
      if |SP1 = on then
        &DATA_SOURCE_TOTAL1 = addr(&CH1)
      elsif |SP2 = on then
        &DATA_SOURCE_TOTAL1 = addr(&CH2)
      else
        &DATA_SOURCE_TOTAL1 = 0
      endsel

      select ... case ... endsel
      select ... case ... default ... endsel

      Syntax
      select_statement ::= select var_reg
      case immediate { "," immediate } ":" statement_sequence
      { case immediate { "," immediate } ":" statement_sequence }
      [ default: statement_sequence ]
      endsel
      var_reg ::= var | var "[" exp "]"
      The SELECT statement allows you to test a register for fixed values. The register can be either integer or floating point and might even be an array index. It needs at least one CASE branch with at least one immediate value as well as the ENDSEL at the end. The optional final DEFAULT case matches any value

      The first match with the actual register value will lead to the execution of that branch. After that the execution continues after the ENDSEL command (unlike the switch statement in C, C++ which needs an explicit break).
      Example:
      select &PumpState[#curPump]
      case pOFF:
        // do nothing
      case pLOW, pMEDIUM:
        write "Pump" + #curPump
      case pHIGH:
        write "Alarm"
      endsel

      Top
    6. Unconditional Jumps

      In contrast to the conditionals above, unconditional jumps allow you to resume command execution at another position in your code. This position has to be identified by a label.

      A label consists of a string of letters, numbers, and underscores which has to be unique and cannot start with a number. It has to be placed by itself on the left hand margin on a new line followed only by a colon. The colon is only used to define the label but it is not part of the label name. All user defined labels are case sensitive. Only the pre-defined macro names (e.g. MAIN_MACRO) are case insensitive to avoid confusion with user defined labels. However, if you jump to pre-defined macro the label name has to match exactly (case sensitive) how you defined it in your code.

      Although these jump commands are unconditional they are often used within conditionals.

      goto

      The GOTO command simply jumps to the label position and continues with the command execution there. This allows you to write code that is very hard to read. However, there are cases where it actually simplifies the code, for example when you encounter a fatal error condition and you have to stop your application completely.
      Example:
      error:
      |Pump1 = off
      |Pump2 = off
      #state = sERROR
      write "ALARM"
      end
      ...
      select #state
      case pHIGH:
        if &Temperature = FATAL then
          goto error
        endif
      endsel

      gosub, return, end

      The GOSUB command (GO to SUBroutine) is similar to the GOTO command. However, before it jumps to the label position it remembers its current position. Then the execution continues at the label position until it encounters and END or RETURN command. At that point it will return back to the position where the GOSUB command was issued and continue with the execution.

      This mechanism - jump to another code segment and return back after you are finished - is usually referred to as a subroutine. The pre-defined macros are basically only special subroutines. The END and RETURN commands are identical, but RETURN reflects the nature of a subroutine (that it returns back to the position where it was called) better. However, we recommend to use END with the pre-defined macros and RETURN with user defined subroutines to indicate this difference.

      Subroutines can be called from within subroutines (also called nested subroutines) but only four levels deep. You can also GOSUB to a pre-defined macro.
      Example:
      RESET_MACRO:
      &TOTAL1 = 0

      reset_cycle:
      #counter = 0
      #cycle = CYCLE_INIT
      end

      MAIN_MACRO:
      if |CAPTURE_PIN = on then
        gosub reset_cycle
      endif
      ...

      In this example the macro will execute only the last part of the RESET_MACRO when the capture pin is connected to common. But it is easier to read when you do it this way:

      RESET_MACRO:
      &TOTAL1 = 0
      // last thing to do, so we don't have to get back
      goto reset_cycle
      end

      reset_cycle:
      #counter = 0
      #cycle = CYCLE_INIT
      return

      MAIN_MACRO:
      if |CAPTURE_PIN = on then
        gosub reset_cycle
      endif
      ...

      Top
    7. Loop Constructs

      In most programming languages loop constructs are essential, either to iterate over a list or array, or to wait for an event. However, Tiger meters already have a fixed cycle time (macro loop) which restricts the execution time for any command sequence. Therefore loop contstructs in Tiger BASIC are only of limited use.

      As the meter is always in a loop, waiting for an event is easily accomplished with a state machine (SELECT) or a simple IF command. But when you want to iterate over a register array within a single macro loop it is your responsibility to make sure that the whole loop can be finished in time.

      You can check the time it takes to execute your macro by polling the &CPU_LOADING register.

      for ... next

      Syntax
      for_loop ::= for var "=" exp to exp [ step exp ]
                     statement_sequence
                   next var
          
      The FOR loop uses a counter variable which is initialized (start value) and then increased with every NEXT command by the STEP value (default is 1). When the counter reached the TO (end) value the loop is terminated and the execution continues after the NEXT statement. As long as the counter is between the start and the end value the command sequence between FOR and NEXT will be executed.
      Example:
      for #counter = 0 to 31
        &TABLE1_OUTPUT1[#counter] = &USER_MEMORY_1[#counter]
      next #counter

      This example updates the output of linearization table 1 with values stored in USER_MEMORY 1 to 32.

      repeat ... until

      Syntax
      repeat_loop ::= repeat
                        statement_sequence
                      until condition
          
      The REPEAT command leads to a so called non-rejecting loop as it executes the command sequence within at least once before checking the condition. If the condition is false the loop back to the repeat command and execute the sequence again.

      As most registers are only updated by the operating system before each macro loop the condition should only rely on register changes by the macro within the loop.
      Example:
      #digits = MAX_VALUE
      print "&CH1 ="
      repeat
        print " "
        #digits = #digits / 10
      until #digits < &CH1
      print &CH1

      In this example the value of CH1 is printed to the serial port with leading spaces so that subsequent calls will output CH1 with right alignment.

      This example also shows, that the REPEAT loop still needs some kind of counter (#digits) for the end condition which has to be initialized, too. Thus a FOR loop or a SELECT statement with all possible values of #digits would improve the readability.

      Top
    8. Special Functions

      include

      The INCLUDE command allows you to include code that is stored in a different file into the current file for compilation. Files can be included recursively up to 10 levels. The included file does not have to be a complete macro. Most likely, included files contain only constants, re-definitions of registers, or subroutines.
      Example:
      include "C:\BasicLibrary\SerialProtocol.bas"

      force_log

      With the FORCE_LOG command the macro triggers the meter to take a log sample.
      Example:
      if &TIMER1 > LOG_TIMEOUT then
        &TIMER1 = 0
        FORCE_LOG
      endif

      abs()

      Syntax
      abs_function ::= abs "(" exp ")"

      The ABS() function returns the absolute value of an expression, i.e. it multiplies the expression with -1 if it is negative.
      Example:
      #diff = abs(&CH1 - &CH2)

      addr()

      Syntax
      addr_function ::= addr "(" var ")"

      The ADDR() function returns the register number of its argument. The register number is needed for DATA_SOURCE registers and to read/write a register of a remote meter.
      Example:
      // show the value of #diff on the display
      &DATA_SOURCE_DISPLAY1 = addr(#diff)

      asc(), chr()

      Syntax
      asc_function ::= asc "(" """ char """ ")"
      chr_function ::= chr "(" ASCII_int ")"

      The ASC() function returns the ASCII value of its character argument. This can be used to set the last digit character.
      The CHR() function returns the character with the ASCII value of its integer argument, it is the reverse function to ASC(). This can be used to add whitespace characters to serial strings. The values of the most common whitespace characters are predefined as constants:
      BELL, CR, FF, LF, ESC, NULL, BS, TAB, VTAB
      Example:
      mem &TEXT_CHARACTER_CH1 = asc("T")

      print "Hello, World!" + CHR(CR) + CHR(LF)

      scale()

      Syntax
      scale_function ::= scale "(" exp "," table_num ")"
      table_num ::= "1" | "2" | "3" | "4"

      With the SCALE() function you can apply a linearization table to a value in the macro.
      Example:
      // apply lintable 2 to the sum of CH1, CH2, and CH3
      #lin_out = scale(&CH1+&CH2+&CH3, 2)

      Top
    9. Display Commands

      append, write

      Syntax
      append_statement ::= append text_string
      write_statement ::= write text_string
      write_port_statement ::= write const_int text_string

      text_string ::= dim_array_name "[" const_int "]"
      text_string ::= const_reg | sub_string { "+" sub_string }
      sub_string ::= quoted_text | var | chr_function
      const_reg ::= var | var "[" const_int "]"

      The WRITE command allows you to display a message to the display. If the text does not fit on the display it is scrolled (the use of leading and trailing spaces is recommended in that case). The text can consist of one or more substrings concatenated with '+'. It can be either a quoted string, the return value of the CHR() function, or a register. In the latter case, the register is replaced with the current value of the register in the text. Arrays can only be used with a constant index.

      If the WRITE command is used with an empty string ("") the display buffer is flushed. This can be used to interrupt a currently scrolled message.

      The APPEND command allows you to add more text to the current message.
      Example:
      dim space[] = [ "   ___", "___   " ]
      const LEADING = 0
      const TRAILING = 1

      write "" // interrupt previous message
      write space[LEADING]
      append "Setpoint 1 = " + &SETPOINT1
      append space[TRAILING]

      Mutliple display meters of the Tiger 320 Series can only write to the default display (top display on DI503T, bottom display on DI602T and DI802T). Dual display meters (DI602T, DI802T) of the Tiger 380 series can write to both displays. The optional port infix after the WRITE command specifies which display should be used. The APPEND command will always use the same display as the last WRITE command.
      Example:
      write 1 "Hello " // top display
      write 2 "World!" // bottom/default display

      It is not possible to scroll messages simultaneously on more than one display at a time.

      In edit mode you cannot write to display 1 as it is used for the edit value.

      edit, edit_numeric, edit_text, exit_edit

      Syntax
      edit_statement ::= edit constant | edit var_reg
      edit_numeric_statement ::= edit_numeric
      edit_text_statement ::= edit_text dim_array_name []
      exit_edit_statement ::= exit_edit [ var_reg ]

      var_reg ::= var | var "[" exp "]"

      The EDIT commands refer to the edit mode of the meter. EDIT starts the edit mode with the mandatory parameter for the initial value and EXIT_EDIT - with an optional register to store the edit value - ends it.

      The EDIT_TEXT command allows you to specify a text array that is shown instead of the numeric edit value (which is used as the index of that array). With EDIT_NUMERIC you can switch back to the standard numeric display.
      Example:
      dim ExitEnter[] = [ "  Exit", " Enter" ] const mEXIT = 0
      const mENTER = 1
      ...
      MAIN_MACRO:
      if |CAPTURE_PIN = on and &STATE = 0 and &EDIT_STATE = 0 then
        edit mEXIT
        &EDIT_MIN = mEXIT
        &EDIT_MAX = mENTER
        edit_text ExitEnter[]
        &STATE = eENTER_SETUP
      endif
      end

      EDIT_MACRO:
      select &STATE
      case eENTER_SETUP:
      exit_edit #temp
      if #temp = mEXIT then
        &STATE = 0
      else
        // continue with next value
      endif
      ...
      endsel
      end

      Top
    10. Communication and parsing commands

      print

      Syntax
      print_statement ::= print text_string
      print_statement ::= print const_reg print_format
      print_port_statement ::= print const_int text_string
      print_port_statement ::= print const_int const_reg print_format

      text_string ::= reg_string | sub_string { "+" sub_string }
      sub_string ::= quoted_text | reg | chr_function
      const_reg ::= var | var "[" const_int "]"
      print_format ::= "," const_int [ "," ASCII_int ]

      The PRINT command is used to send ASCII data to the serial output. Similar to the WRITE command strings are concatenated with '+' and register names (const_reg) are replaced with the value of the register. However, subsequent PRINT commands are concatenated (so there is no need for an APPEND).

      The PRINT command with an empty string ("") argument resets the serial buffer (but not the serial pointer!). Please note that the meter has only one serial buffer for transmit and receive (half duplex communication).

      For meters with multiple serial ports (not available on Tiger 320) the intended port can be specified with the port infix (defaults to 1 if omitted). Each serial port has its own buffer, flags, and pointers.
      Example:
      print "" // flush the buffer
      print "Channel 1 = " + &CH1 + chr(CR) + chr(LF)

      print 2 "Hello, World!" // print to second port

      The formatted print allows you to restrict the width of the printed register. The first parameter are the number of digits, it is positive for right alignment and negative for left alignment. The optional second parameter is the ASCII character that is used to fill up any spaces (default is 'SPACE' = 32).
      Example:
      // print CH1 left aligned, 8 chars wide, use '-' to fill up the trailing spaces
      print &CH1, -8, asc("-")

      serial_input

      Syntax
      serial_condition ::= serial_input [ const_int ] str_op comp_string [ "+" comp_string ]
      str_op ::= "=" | <>
      comp_string ::= quoted_string | chr_function

      With the SERIAL_INPUT command you can compare the RECEIVE_BUFFER with a constant string. It starts with the first character of the buffer with a case sensitive match. If the string in the buffer is longer than the compare string the remaining characters are ignored.

      For meters with multiple serial ports (not supported on Tiger 320) the optional port infix can be used to specify the serial buffer.
      Example:
      if SERIAL_INPUT 2 = "SR" then
        // string in serial buffer 2 starts with 'SR'
      endif

      float(), hex(), integer(), serial_pointer

      Syntax
      parse_func ::= num_func "(" serial_pointer [ const_int ] ["," const_int ] ")"

      num_func ::= float | hex | integer

      Starting with the Tiger 380 these extended parsing functions are available to convert strings into numbers. The SERIAL_POINTER (with optional port infix) points to the position in the buffer where the parsing starts, the second parameter indicates the maximum number of characters to be parsed. If the parsing function finds a valid number representation, it returns that number value and sets the SERIAL_POINTER after the parsed section.

      The INT function ignores any decimal points in the pattern, the FLOAT function can handle only one decimal point as well as a two digit signed exponent (e.g. -123.456e-10), the HEX function reads only hexadecimal numbers (0-9a-fA-F) and is limited to 8 chars as the Tiger can only store 32bit registers.

      Please be aware that there is a &SERIAL_POINTER register, too. While this contains the actual index (which can be read or written to), the special command SERIAL_POINTER without the '&' is to be used with the parsing functions.
      Example:
      // read integer from index 8 to index 13 of buffer 2
      &SERIAL_POINTER2 = 8
      #data = int(serial_pointer 2, 6)

      // read float variable from current position in default buffer (port 1)
      %scale = float(serial_pointer)

      modbus_read(), modbus_write()

      Syntax
      mb_read ::= modbus_read "(" const_int "," const_int "," var [ "," mb_flag ]")"
      mb_write ::= modbus_write "(" const_int "," var "," const_int [ "," mb_flag ]")"

      mb_flag ::= MB_BYTE | MB_SHORT | MB_24 | MB_24_SWAPPED | MB_LONG | MB_LONG_SWAPPED | MB_FLOAT | MB_FLOAT_SWAPPED

      With the Tiger 380 we introduced a Modbus Master mode. The two modbus functions allow you to read or write a modbus register of a remote device. The first paramter is the device ID of the remote meter, the second parameter is the source (the address to read or the local register to write), and the third paramter is the target (where the read data is to be stored or the register that is written on the remote device).

      The optional fourth parameter specifies the data type. If it is not specified the type of the local register is assumed. While the Modbus standard only allows for 16bit registers, many Tiger registers are 24 or 32 bit. Therefore two consecutive 16bit registers are read/written to transmit the additional data. Pleas note that for this reason the 24/32bit modbus register numbers on the Tiger 320 differ from the register numbers in ASCII mode.

      An optional port infix can be used to specify the serial buffer.

      Both commands should only be invoked within the MODBUS_MASTER_MACRO which can wait for the reply longer than a macro loop.

      Please note that the accessible register addresses use the default offset of 40000. Thus the ADDR() function can be used to determine the address to talk to another Tiger meter of the same series. Coil/bit addresses (0x and 1x range) are not supported, addresses in the 3x range have to be specified explicitely with the 30000 offset.
      Example:
      const TIGER320_RESULT = 40513

      MODBUS_MASTER_MACRO:
      |LED1 = off : |LED2 = off
      if #modbus = mbREAD then
        // read CH3 from other Tiger 380
        modbus_read 2 (5, addr(&CH3), &RESULT)
        |LED1 = on
      elsif #modbus = mbWRITE then
        // write to a Tiger 320
        modbus_write 2 (7, &RESULT, TIGER320_RESULT)
        |LED2 = on
      endif
      end

      See our Tiger BASIC Tutorial for further details.
Top
Vista, CA, 2005 March 21

App Documentation