Table Of Contents:
General info
About variables
Mathematical and logical operations
Standard Basic language elements
Complex expressions evaluation
Subroutines
Microcontroller related language elements
Special Basic language elements
Structured language support (procedures and functions)
Using internal A/D converter module
Software UART implementation
I2C communication with external I2C devices
Support for software master SPI (Serial Peripheral Interface) communication
Interfacing Radio Control (R/C) servos
Interfacing Stepper Motors
#define directive parameters
Library support
Table Of Contents:
• General info
• About variables
• Mathematical and logical operations
• Standard Basic language elements
• Complex expressions evaluation
• Subroutines
• Microcontroller related language elements
• Special Basic language elements
• Structured language support (procedures and functions)
• Using internal A/D converter module
• Software UART implementation
• I2C communication with external I2C devices
• Support for software master SPI (Serial Peripheral Interface) communication
• Interfacing Radio Control (R/C) servos
• Interfacing Stepper Motors
• #define directive parameters
• Library support
• General info
Basic compiler editor is composed of editor panel (for user program editing) and source explorer (for easy navigation through all elements of user program - variables, symbols, constants, subroutines, procedures and functions). Editor formats and colorizes entered lines of user program, that simplifies the debugging process.
In all the user-defined program element names, that is in all the variable names, names of the procedures, functions, subroutines, constants, symbols and labels, both lower-case and upper-case characters can be used, along with the underscore and numeric characters. A numeric character will not be accepted by the compiler to be the leading character in the element name.
The primary output of the compiler is an assembler source file. However, with an appropriate command from the menu it can be assembled and even loaded in the simulator with a single click. Menu commands and options are rich, as well as the commands from the right-click pop-up menus for the editor and source explorer. Basic compiler's assembler output contains many useful comment lines, that makes it very helpful for educational purposes, also.
Show Warnings
If Show Warnings option is enabled, in the Warnings window Basic compiler will show information about unused declarations, subroutines, procedures and functions in the user basic program.
Do Not Compile Unused Code
If this option is enabled, Basic compiler will not compile unused declarations, subroutines, procedures and functions, in order to save memory resources.
Initialize Variables On Declaration
If this option is enabled, Basic compiler will reset to zero all memory locations allocated for variables, at the position of their declaration in the basic program. This option is useful for beginners, because RAM memory is filled with random values at device power-up, and it is easy to make a mistake to assume that all variables are reset to zero at power-up. Experienced users can save some program memory, by disabling this option and taking control of variable initial values by user program where necessary.
Optimize Variables Declaration
This option will turn on the compiler internal routine that will optimize the variables declaration order based on the usage frequency of the variables. In this way, the most frequently used variables will be stored in lower RAM memory locations, resulting in possibly smaller size of the generated code.
Dynamic Temporary Variables Management
If dynamic management is enabled, temporary variables created by compiler will not reserve allocated RAM memory permanently. Allocated memory will be released immediately after use, thus minimizing the total RAM memory required to compile the program. As a consequence, temporary compiler variables will not be listed in the Watch Variables tool.
Optimize Program Memory Paging
If this option is enabled, the compiler internal routine will optimize the management of STATUS register bits 5 and 6, that control the execution of GOTO and CALL instructions for programs using more than one program memory page. It is advised to turn on this optimization routine only if the generated code size came close to using almost all available program memory, because it can noticeably increase the total program compilation time. However, its execution will have a significant impact on the size of the generated code, making additional space for more basic program lines.
• About variables
The following data types are supported:
Bit - 1-bit, 0 or 1
Byte - 1-byte integers in the range 0 to 255
Word - 2-byte integers in the range 0 to 65,535
Short - 1-byte signed integers in the range -128 to 127
Integer - 2-byte signed integers in the range -32,768 to 32,767
Byte, Word, Short and Integer variable types will be sometimes referred to as 'all integer data types' or 'all numeric data types'.
Variables can be global (declared in the main program, before the End statement) or local (declared in subroutines, procedures and functions). Variable name used for a variable with global scope can be used again for local variable names. The compiler will reserve separate memory locations for them. The total number of variables is limited by the available microcontroller RAM memory. Variables are declared using DIM statement:
Dim i As Bit
Dim j As Byte
Dim k As Word
Dim statement allows multiple declarations in one line of code by using comma-separated list of variable names:
Dim k1, k2, k3 As Word
If necessary, variable address can be specified during declaration:
Dim x As Byte @ 0x1f
Using arrays of variables is not available in PIC10 basic compiler.
RESERVE statement allows advanced usage by reserving some of the RAM locations to be used by in-code assembler routines or by MPLAB In-Circuit Debugger. For example:
Reserve 0x1f
High and low byte of a Word variable can be addressed by .HB and .LB extensions (dot notation). Individual bits can be addressed by .0, .1, ..., .14 and .15 extensions. It is possible to make conversions between Byte and Word data types using .LB and .HB extensions or directly:
Dim x As Byte
Dim y As Word
x = y.HB
x = y.LB 'This statement is equivalent to x = y
y.HB = x
y.LB = x
y = x 'This statement will also clear the high byte of y variable
All special function registers (SFRs) are available as Byte variables in basic programs. Although not mapped in SFR memory, OPTION_REG, TRISIO, TRISB and TRISC registers can be used as write-only Byte variables. Individual bits of a Byte variable can be addressed by .0, .1, .2, .3, .4, .5, .6 and .7 extensions or using official names of the bits:
Dim x As Bit
Dim y As Byte
x = y.7
y.6 = 1
TRISIO.1 = 0
TRISB = 0
PORTB.1 = 1
PORTB = 255
STATUS.C = 0
OPTION_REG.PSA = 0
Standard short forms for accessing port registers and individual chip pins are also available (RB and RC can be used as Byte variables; GP0, GP1, GP2, ..., RB0, RB1, RB2, ..., RC6, RC7 are available as Bit variables):
RB = 0xff
GP0 = 1
It is possible to address/index individual bits in Byte and Word data type variables with a Byte or Word index variable using the dot (.) operator (dot notation). Indexing is 0-based. Index variable value should be in 0-7 range for bits in Byte variables, and 0-15 range for bits in Word variables.
Example 1:
Dim i As Byte
Dim j As Byte
i = 0xff
For j = 0 To 7
i.j = 0
Next j
Example 2:
Dim i1 As Word
Dim i2 As Word
Dim j As Byte
i1 = 0x55aa
For j = 0 To 15
i2.j = i1.j
Next j
It is possible to use symbolic names (symbols) in programs, to easily address system variables, or to create aliases for variables of all available data types. Symbols can be global or local. SYMBOL directive is used to declare symbolic names:
Symbol led1 = PORTB.0
led1 = 1
Constants can be used in decimal number system with no special marks, in hexadecimal number system with leading 0x or leading $ notation (or with H at the end) and in binary system with leading % mark (or with B at the end). ASCII value of a character can be expressed in string format (e.g. "A"). Keywords True and False are also available for Bit type constants. For example:
Dim x As Bit
Dim y As Byte
x = True
y = 0x55
y = %01010101
y = "Y"
Constants can be assigned to symbolic names using CONST directive. Constants can be global or local. One example:
Dim x As Word
Const highval = 1023
x = highval
It is possible to use comments in basic source programs. The comments must begin with single quote symbol (') and may be placed anywhere in the program.
Comment sign '//' is an alternative for the standard single quote sign.
Lines of assembler source code may be placed anywhere in basic source program and must begin with ASM: prefix. If labels are used, no space should be left between the ASM: prefix and the label. For example:
ASM: NOP
ASM:LABEL1: MOVLW 0xFF
Symbolic names of all variables, symbols and constants (global and local) can be used as the arguments of the assembler instructions. This is also valid for Bit variables and bit-oriented assembler instructions. The compiler will replace that symbolic name with the proper variable address or constant value:
Dim varname As Byte
varname = 0
ASM: MOVLW 0xFF
ASM: MOVWF VARNAME
When working with inline assembler code, it could be useful to use working register as a source or destination in assign statements. For that purpose WREG keyword should be used and the compiler will take care of the bank control:
Dim varname As Byte
ASM: MOVLW 0xFF
varname = WREG
If large amount of assembler code should be used, it can be loaded from an external assembler file and included to the current program by using IncludeASM directive. Its only argument is a string containing the path to the external .ASM file. This can be the full path or only the file name, if the external file is located in the same folder as the current basic program file. During the compilation process the external assembler code will be appended to the current program at its end, and not at the position of the directive. Multiple files can be included with separate IncludeASM directives. External assembler files should not contain ASM: prefix used for inline assembler code. It is also strongly suggested not to use ORG directives in the external assembler code. For example:
IncludeASM "test.asm"
IncludeASM "d:\example\test2.asm"
• Mathematical and logical operations
Five arithmetic operations (+, -, *, /, MOD) are available for all integer data types. The compiler is able to compile all possible complex arithmetic expressions, including those containing math functions and user-defined functions. For example:
Dim i As Word
Dim j As Word
Dim x As Word
i = 123
j = i * 234
x = 2
x = (j * x - 12345) / (i + x)
Square root of an integer number (0-65535 range) can be calculated using SQR function:
Dim x As Word
x = 3600
x = Sqr(x)
For Bit data type variables four logical operations are available. It is possible to make only one logical operation in one single statement. Logical operations are also available for other variable types. For example:
Example 1:
Dim i As Bit
Dim j As Bit
Dim x As Bit
x = Not i
x = i And j
x = i Or j
x = i Xor j
Example 2:
Dim x As Word
Dim y As Word
x = x Or y
PORTB = PORTC And %11110000
SHIFTLEFT and SHIFTRIGHT functions can be used to shift bit-level representation of a variable left and right. The first argument is input variable and the second argument is number of shifts to be performed. Here are two examples:
Example 1:
TRISB = 0x00
PORTB = %00000011
goleft:
WaitMs 250
PORTB = ShiftLeft(PORTB, 1)
If PORTB = %00110000 Then Goto goright
Goto goleft
goright:
WaitMs 250
PORTB = ShiftRight(PORTB, 1)
If PORTB = %00000011 Then Goto goleft
Goto goright
Example 2:
TRISB = 0x00
PORTB = %00000001
goleft:
WaitMs 250
PORTB = ShiftLeft(PORTB, 1)
If PORTB.5 Then Goto goright
Goto goleft
goright:
WaitMs 250
PORTB = ShiftRight(PORTB, 1)
If PORTB.0 Then Goto goleft
Goto goright
There are three statements that are used for bit manipulation - HIGH, LOW and TOGGLE. If the argument of these statements is a bit in one of the PORT registers (or GPIO), then the same bit in the corresponding TRIS register is automatically cleared, setting the affected pin as an output pin. Some examples:
High PORTB.0
Low GP1
• Standard Basic language elements
Unconditional jumps are performed by GOTO statement. It uses line label name as argument. Line labels can be global or local. Line labels must be followed by colon mark ':'. Here is one example:
Dim x As Word
x = 0
loop: x = x + 1
Goto loop
Four standard BASIC structures are supported: FOR-TO-STEP-NEXT, WHILE-WEND, IF-THEN-ELSE-ENDIF and SELECT CASE-CASE-ENDSELECT. Here are several examples:
Example 1:
Dim x As Byte
TRISB = 0
x = 255
While x > 0
PORTB = x
x = x - 1
WaitMs 100
Wend
PORTB = x
Example 2:
TRISB = 0
loop:
If PORTC.0 Then
PORTB.0 = 1
Else
PORTB.0 = 0
Endif
Goto loop
Example 3:
Dim x As Word
TRISB = 0
For x = 0 To 10000 Step 10
PORTB = x.LB
Next x
Example 4:
Dim i As Byte
Dim j As Byte
Dim x As Byte
j = 255
x = 2
TRISB = 0
For i = j To 0 Step -x
PORTB = i
Next i
Example 5:
Dim x As Byte
loop:
Select Case x
Case 255
x = 1
Case <= 127
x = x + 1
Case Else
x = 255
EndSelect
Goto loop
For statement will accept all numeric data types for the running variable. Exit For statement provides a way to exit a For-Next loop. It transfers control to the statement following the Next statement.
After IF-THEN statement in the same line can be placed almost every other possible statement and then ENDIF is not used. There are no limits for the number of nested statements of any kind. Six standard comparison operators are available: =, <>, >, >=, <, <=.
Also, the compiler is able to evaluate math/arithmetics complex expressions on both sides of the comparison operator in While and If-Then statements.
Case statement will accept complex expressions, multiple comma-separated conditions, ranges of values in the form exp1 To exp2:
Select Case x
Case 255, 254, < y * 2
Case y + 1, y + 2, y + 5 To y + 10
If there is a need to insert an infinite loop in basic program, that can be done with HALT statement.
• Complex expressions evaluation
Complex expressions evaluation engine can evaluate all possible and meaningful expressions containing any number of supported operators, operands, functions and parentheses.
The expression evaluation engine will accept both Basic and C-inspired syntax for the operators.
Here is the list of all supported operators with their alternative forms:
- Arithmetic operators: +, -, *, /, % (Mod), ++ (post or pre-increment), -- (post or pre-decrement), unary +, unary -
- Assignment operators: =, +=, -=, *=, /=
- Comparison operators: == (=), != (<>), <, <=, >, >=
- Logical operators: unary ! (Not), && (And), || (Or)
- Bitwise operators: unary ~ (Not), & (And), | (Or), ^ (Xor), << (ShiftLeft), >> (ShiftRight)
The dot operator (.) is available for addressing individual bits, bytes and words in variables.
On low level, implemented expression evaluation engine is C-language based, following C standards for operator precedence.
In assignment statements, the engine will initially consider Not, And and Or as bitwise operators.
In condition expressions of While and If-Then statements, Not, And and Or will be initially considered as logical operators.
This information may only be important from an operator precedence perspective. Actually, the expression evaluation engine will not generate errors related to the distinction between logical and bitwise operators. The operation performed is determined by the type of the operands.
Complex expressions are generally accepted everywhere. Complex expressions can be used as direct arguments when calling procedures and functions in the code, and in all statements where comma-separated list of arguments is accepted, like Serout and I2CWrite.
While statement will accept all possible complex expressions that evaluate to Bit data type. The same is valid for If-Then statement. Case statement can also contain complex expressions.
For statement will accept all possible complex expressions for the initial expression, for the To argument, and for the optional Step argument as well.
Illustrative lines of code:
Serout func1(i * 2 + 1), 32, a1++, 32, --a2
Case 2, 3, >= 10 + j
If w1 = CWord b1 * 100 Then i = 0
For i = j + 1 To k * 2 + 1 Step 2
The compiler will accept lines containing the expression without the assignment operator. The expression will just be evaluated if possible, and if not, the Syntax error message will be displayed. So, it is possible to have lines of code like these ones:
i++
x > y
Along with the standard basic For-Next loops, it is possible to use the C-like CFor-CNext loops. Init expression, condition expression and loop expression should be separated by the semicolon ';' symbol. In addition, the init and loop expressions can contain multiple statements separated by commas.
For example:
Dim i As Byte
Dim j As Byte
Dim k As Byte
k = 0
CFor (i = 1; i < 7; i++)
k++
CNext
k = 0
CFor (i = 5, j = 10; i + j < 20; i++, j++)
k++
CNext
Unary operators are treated as functions. The opposite is also true. So, the evaluation engine will accept the following format, also:
y = Sqr x
If needed, temporary system variables will be declared and used during the expression evaluation. Optimal management of temporary variables has been achieved by the evaluation engine.
Data type promotion
Numeric data types are ordered from the lowest to the highest in the following sequence: Byte, Word. Particular arithmetic or logical operation is performed on the higher data type level of the operands (variables and constants).
In case when only arithmetic operators remain until the end of the evaluation, the evaluation engine will promote the data type of temporary variables to the result variable data type. So, one can freely write:
Dim b1 As Byte
Dim w1 As Word
'b1 * 100 will be calculated on the word data type level; desired precision will be achieved
w1 = b1 * 100 + 500
However, that will not happen in case of comparison operators, like in the example:
Dim b1 As Byte
Dim w1 As Word
'left-hand side will be evaluated to word; right-hand side to byte; desired precision will be probably lost
While w1 - 1 = b1 * 100
Wend
For similar situations, one can make use of the data type conversion functions (unary operators): CByte, CWord, CShort, CInteger.
This is the updated example:
While w1 + 1 = CWord b1 * 100
Wend
• Subroutines
Structured basic programs can be written with subroutines. When using subroutines, the main routine must be ended with END statement, and subroutines must be placed after END statement in program. END statement is compiled as an infinite loop. Subroutines should be declared with SUB statement followed by the subroutine name. All variables declared in a subroutine have local scope, so they don't need to have unique names. Subroutines must be ended with END SUB statement. They can be conditionally exited with EXIT SUB statement. Calls to subroutines are implemented with GOSUB statement.
For backward compatibility, global scope line labels will still be recognized as subroutine declarations, GOSUB statements will also accept the line label names as arguments and RETURN statement can be used for the final return from a subroutine. Users need to take care that the program structure is consistent when using local scope line label as GOSUB argument (the compiler will accept that, however an applicable warning will be generated).
Some basic statements that use more than one stack level can not be used in subroutines. The compiler will display an appropriate error message. Calling subroutines from a subroutine is not allowed.
Here are two examples:
Example 1:
Dim x1 As Byte
Dim x2 As Byte
Dim x3 As Byte
For x1 = 0 To 10
Gosub calculate_x2x3
Next x1
End
Sub calculate_x2x3
x2 = 100 + x1
If x1 > 5 Then Exit Sub
x3 = x2
End Sub
Example 2:
Symbol ad_action = ADCON0.GO
Symbol display = PORTC
TRISC = %00000000
TRISB = %00111111
ADCON0 = 0xf0
High ADCON0.ADON
main:
Gosub getadresult
display = ADRES / 4
Goto main
End
getadresult:
High ad_action
While ad_action
Wend
Return
• Microcontroller related language elements
Microcontroller ports and pins can be configured as inputs or outputs by assigning proper values to TRISx registers or their bits. That task can also be accomplished by a CONFIGPIN statement. Its syntax is apparent from the following examples:
ConfigPin PORTB = Output
ConfigPin GP0 = Output
ConfigPin PORTC.3 = Input
ConfigPin GPIO = Input
All PIC microcontrollers that feature analog capabilities (A/D converters and/or analog comparators) are setup at power-up to use the involved pins for these analog purposes. In order to use those pins as digital input/outputs, they should be setup for digital use by changing the values in some of the special functions registers as specified by the datasheets. To setup all pins for digital purposes, All_Digital statement (alias: AllDigital) can be used at the beginning of the basic program.
Working with a microcontroller pin as it is a digital input or output when it is configured to be used for analog purposes, will most probably lead to unexpected results, because reading the appropriate bit of the PORT register will always return zero. One should remember here that all microcontroller instructions that specify PORT (or any other) register as part of the instruction perform a read-modify-write operation.
There is a configuration word parameter CONFIG (aliases: CONFIG1, CONF_WORD) that can be set using #define directive (alias: Define) to override the default value. If it is necessary to calibrate the internal oscillator by changing the default value in the OSCCAL register (0xFE), there is OSCCAL_VALUE parameter available for that purpose. The clock frequency of the target device can be specified by setting the CLOCK_FREQUENCY parameter (the value is expressed in MHz). These parameters should be setup at the beginning of the basic program. For example:
#define CONFIG = 0x008
#define OSCCAL_VALUE = 0xfe
#define CLOCK_FREQUENCY = 4
The full list of all available parameters for the #define directive, along with their default values and allowed ranges of values, can be found in the last topic of this document.
• Special Basic language elements
WAITMS and WAITUS statements can be used to force program to wait for the specified number of milliseconds or microseconds. It is also possible to use variable argument of Byte or Word data type. These routines use Clock Frequency parameter that can be changed from the Options menu. WAITUS routine has minimal delay and step that also depend on the Clock Frequency parameter.
Dim x As Word
x = 100
WaitMs x
WaitUs 50
Important Note: When writing programs for real PIC devices you will most likely use delay intervals that are comparable to 1 second or 1000 milliseconds. Many examples in this help file also use such 'real-time' intervals. But, if you want to simulate those programs you have to be very patient to see something to happen, even on very powerful PCs available today. For simulation of 'WaitMs 1000' statement on 4MHz you have to wait the simulator to simulate 1000000 instructions and it will take considerable amount of time even if 'extremely fast' simulation rate is selected. So, just for the purpose of simulation you should recompile your programs with adjusted delay intervals, that should not exceed 1-10ms. But, be sure to recompile your program with original delays before you download it to a real device. There is an easy way to change arguments of all WAITMS statements in a large basic program with a value in the range 0-10 for simulation purposes. With one line of code setting parameter SIMULATION_WAITMS_VALUE with #define directive, the arguments of all WAITMS statements in the program will be ignored and the specified value will be used instead during compiling. Omitting that line (say, with the comment sign) will cancel its effect and the compiled code will be ready again for the real hardware.
It is possible to insert breakpoints for the simulator directly in basic programs using BREAK statement. It is compiled as reserved opcode 0x001 and the simulator will interpret this opcode as a breakpoint and switch the simulation rate to Step By Step.
LOOKUP function can be used to select one from the list of Byte constants, based on the value in the index Byte variable, that is supplied as the last separated argument of the function. The first constant in the list has index value 0. The selected constant will be loaded into the result Byte data type variable. The index variable must contain the value in the proper range. Here is one small example:
Dim index As Byte
Dim mask As Byte
TRISB = %00000000
loop:
For index = 0 To 3
mask = LookUp(0x21, 0x82, 0x0c, 0x82), index
PORTB = mask
WaitMs 1000
Next index
Goto loop
If all constants in the list (or part of them) are ASCII values, then shorter form of the list can be created by using string arguments. For example:
mask = LookUp("ABCDEFGHIJK"), index
If it is necessary to count the number of pulses that come to one of the micrcontroller's pins during a certain period of time, there is COUNT statement available for that purpose. The selected pin is defined using #define directive to setup COUNT_REG and COUNT_BIT parameters. COUNT statement will setup the pin as an input pin. It has two arguments. The first one defines the duration of the observation expressed in milliseconds and it must be a numeric constant in the range 1-10000. The second argument is a Byte or Word variable where the counted number of pulses will be stored after its execution. COUNT statement uses internal Timer0 peripheral module. There is COUNT_MODE parameter available that can be setup with #define directive. If it is set to value 1 (default value) COUNT statement will count the number of rising pulse edges. If COUNT_MODE = 2, the number of falling edges will be counted.
#define COUNT_REG = PORTB
#define COUNT_BIT = 0
#define COUNT_MODE = 1
Dim num_of_pulses As Word
Count 1000, num_of_pulses
FREQOUT statement can be used to generate a train of pulses (sound tone) with constant frequency and specified duration on the pin that is defined with FREQOUT_REG and FREQOUT_BIT parameters using #define directive. The statement will setup the pin as an output pin. The first argument specify the tone frequency and it must be a constant in the range 1-10000Hz. The second argument defines the tone duration and it also must be a numeric constant in the range 1-10000ms. Choosing higher tone frequencies with low microcontroller clock frequency used may result in somewhat inaccurate frequency of the generated tones. FREQOUT statement can be alternatively used in 'variable mode' with Word data type variables instead of constants as arguments. In this mode of usage the first argument is supposed to hold the half-period of the tone (in microseconds) and the second argument must hold the total number of pulses that will be generated. FREQOUT statement can not be used in subroutines. The following code will generate one second long tone on RB0 pin with 600Hz frequency:
#define FREQOUT_REG = PORTB
#define FREQOUT_BIT = 0
FreqOut 600, 1000
• Structured language support (procedures and functions)
Procedures can be declared with PROC statement. They can contain up to 30 arguments (comma-separated list) and all available data types can be used for argument variables. Argument variables are declared locally, so they do not need to have unique names in relation to the rest of user basic program, that makes very easy to re-use once written procedures in other basic programs. Procedures can be conditionally exited with EXIT statement. They must be ended with END PROC statement and must be placed after END statement in program. Calls to procedures are implemented with CALL statement. The passed arguments can be variables, numeric constants or complex numeric expressions. For example:
Dim x As Byte
TRISB = 0
For x = 0 To 255
Call portb_display(x)
WaitMs 100
Next x
End
Proc portb_display(arg1 As Byte)
PORTB = arg1
End Proc
All facts stated for procedures are valid for functions, also. Functions can be declared with FUNCTION statement. They can contain up to 30 arguments and argument variables are declared locally. Functions can be exited with EXIT statement and must be ended with END FUNCTION. The value returned by a function should be assigned to the function name in the function code, or provided as argument of ReturnValue statement. The name of the function is declared as a global variable, so if the function is called with CALL statement, after its execution the function variable will contain the result. Standard way of function calls in assignment statements can be used, also. One simple example:
Dim x As Byte
Dim y As Word
For x = 0 To 255
y = square(x) + 1
Next x
End
Function square(arg1 As Word) As Word
square = arg1 * arg1
'or ReturnValue arg1 * arg1
End Function
The default mechanism is to pass an argument to a procedure by value. Either no prefix or ByVal prefix can be used for that argument. The procedure will use the argument value and will not change the input value of the calling variable. There are two passing mechanisms for passing arguments to procedures by reference - ByRef and ByRefOut. When ByRef prefix is used to pass a variable argument to a procedure, the procedure will use the input variable value, but it can also change the value of the variable during the procedure execution. The calling variable will be exposed to change. When ByRefOut prefix is used for the procedure argument, the input value of the calling variable will not be passed to the procedure at all. The procedure will only return an output value to the calling code through that argument. So, ByRefOut mechanism should be used when the procedure has a genuine need to output only a value to the calling code, and in that way the result will be a more optimized code compared to the use of the standard ByRef mechanism. ByRef and ByRefOut can be used for function arguments as well.
One test example:
Dim in_only As Byte
Dim inc_me As Byte
Dim add_inc_me_and_in_only As Byte
in_only = 5
inc_me = 10
Call testbyref(in_only, inc_me, add_inc_me_and_in_only)
'after this call
'inc_me = 11
'add_inc_me_and_in_only = 16
End
Proc testbyref(arg1 As Byte, ByRef arg2 As Byte, ByRefOut arg3 As Byte)
arg2 = arg2 + 1
arg3 = arg1 + arg2
End Proc
Procedures (and functions) can also be called without the Call statement. In that case, the procedure name should be followed by the comma-separated list of arguments.
The lines of code with the same effect:
Call portb_display(x)
portb_display x
Basic source code from an external file can be included to the current program by using INCLUDE directive. Its only argument is a string containing the path to the external .BAS file. This can be the full path or only the file name, if the external file is located in the same folder as the current basic program file. During the compilation process the external basic source will be appended to the current program. Multiple files can be included with separate INCLUDE directives. To maintain the overall basic code structure, it is strongly suggested that the external file contains global declarations, subroutines, procedures and functions, only. Here is one very simple example for the demonstration:
main.bas:
Dim i As Word
Dim j As Word
Include "inc1.bas"
Include "inc2.bas"
For i = 1 To 10
j = func1(i, 100)
Call proc1(j)
Next i
End
inc1.bas:
Dim total As Word
Proc proc1(i As Word)
total = total + i
End Proc
inc2.bas:
Function func1(i As Word, j As Word) As Word
func1 = i + j
End Function
• Using internal A/D converter module
ADC_Read statement (alias: Adcin) is available as a support for internal A/D converter. This statement is implemented in OshonSoft basic library files (see Library support section). Its first argument is ADC channel number that can be an arbitrary expression and the second argument is a variable that will be used to store the result of A/D conversion. ADC_Read statement uses two parameters: ADC_Clk and ADC_Sample_uS (aliases: ADC_CLOCK, ADC_SAMPLEUS). Their default values and allowed ranges are defined in ADC Module library and this information is shown in Libraries info panel (for some PIC models ADC_Clk parameter is not available). The default parameter value can be changed using #define directive. ADC_Clk parameter determines the choice for ADC clock source. ADC_Sample_uS parameter sets the desired ADC acquisition time in microseconds. ADC_Read result should be byte type variable (refer to Lib info panel). ADC_Read statement presupposes that the corresponding pin is configured as an analog input (TRIS register and ANSx bits in ADCON0 register). ADC_Read statement can not be used in subroutines. Here is one example:
Dim adresult1 As Byte
Dim adresult2 As Byte
#define ADC_Sample_uS = 50
ADCON0.ANS0 = 1
ADCON0.ANS1 = 1
TRISIO = 0x0f
ADC_Read 0, adresult1
ADC_Read 1, adresult2
• Software UART implementation
On all supported PIC devices software serial communication can be implemented with SEROUT and SERIN statements. Prior to using SEROUT statement, SEROUT_REG, SEROUT_BIT and SEROUT_BAUD parameters should be setup with #define directive to define microcontroller serial output pin and output baud rate selection. The analogous parameters for SERIN statement are SERIN_REG, SERIN_BIT and SERIN_BAUD. All baud rates in the range 100-200000 will be accepted for baud rate parameters, but it is suggested to use the standard baud rates like: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200. Using higher baud rates with low clock frequency could cause inaccurate timing and framing errors. Small adjustments (1-5%) could possibly fix the problem.
SEROUT statement is followed by the list of arguments to be sent to serial port. One can use string variables and constants, numeric variables and byte numeric constants. Prefix '#' used in other PIC compilers to send the decimal string representation of a variable to the serial port is not available in this compiler due to only two stack levels featured in the supported devices. SEROUT statement uses SEROUT_DELAYUS parameter that has default value of 1000 microseconds. This defines the delay interval before a character is actually sent to the port and it is used to increase the reliability of software SEROUT routine.
String constants should begin and end with the double quotation marks. There are four symbolic string constants available: Qt (or """") for the double quotation mark (ASCII code 34), CrLf for the carriage return - line feed sequence (ASCII codes 13-10), Cr for the carriage return (ASCII code 13) and Lf for the line feed character (ASCII code 10). User-defined names for the string constants with the Const directive can also be used.
SERIN statement should be supplied with the list of numeric data type variables to be loaded with the values received on serial port. This statement will wait until the required number of bytes is received on serial port. For serial interface with inverted logic levels there are SERININV and SEROUTINV statements available. These four statements can not be used in subroutines.
Here are some examples:
Example 1:
#define SEROUT_REG = GPIO
#define SEROUT_BIT = 0
#define SEROUT_BAUD = 1200
Serout "Hello world!", CrLf
Example 2:
#define SEROUT_DELAYUS = 500
#define SEROUT_REG = PORTB
#define SEROUT_BIT = 1
#define SEROUT_BAUD = 9600
#define SERIN_REG = PORTB
#define SERIN_BIT = 2
#define SERIN_BAUD = 9600
Dim i As Byte
Dim j As Byte
loop:
Serin i
j = i + 48
Serout "Number: ", j, CrLf
Goto loop
SERIN and SERININV statements will wait indefinitely until the required number of bytes is received on software serial port. SERIN_TIMEOUT_REG and SERIN_TIMEOUT_BIT parameters for the #define directive provide a means to implement a custom timeout feature for these statements. When the predefined bit (symbolic bit names can be used here) in any of the available device registers becomes set, the software UART input statement will be interrupted. So, it is possible to use one of the device Timer modules, and program it to overflow after the desired timeout has been reached. In that case, the Timer overflow bit should be assigned to the timeout parameters. For example:
#define SERIN_TIMEOUT_REG = TMR0
#define SERIN_TIMEOUT_BIT = 7
• I2C communication with external I2C devices
I2C communication can be implemented in basic programs using I2CWRITE and I2CREAD statements. Prior to using them, I2C_SDA_REG, I2C_SDA_BIT, I2C_SCL_REG and I2C_SCL_BIT parameters should be setup with #define directive to define microcontroller pins connected to the SDA and SCL pins of the external I2C device. The first argument of both statements must be a constant value or Byte variable called 'slave address'. Its format is described in the datasheet of the used device. For example, for EEPROMs from 24C family (with device address inputs connected to ground) the value 0xA0 should be used for slave address parameter. Both statements will take control over bit 0 of slave address during communication. The second argument of both statements must be a Byte or Word variable (this depends on the device used) that contains the address of the location that will be accessed. If a constant value is used for address parameter it must be in Byte value range. The last argument of I2CWRITE statement is a Byte constant or variable that will be written to the specified address, and for I2CREAD statement it must be a Byte variable to store the value that will be read from the specified address. It is allowed to use more than one 'data' argument. For I2C devices that do not support data address argument there is short form of I2C statements (I2CWRITE1 and I2CREAD1) available where slave address argument is followed with one or more data arguments directly. For some I2C slave devices it is necessary to make a delay to make sure device is ready to respond to I2CREAD statement. For that purpose there is I2CREAD_DELAYUS parameter that can be set by #define directive and has default value of 0 microseconds. Also, for slower I2C devices, it might be necessary to use longer clock pulses. That can be done by setting I2CCLOCK_STRETCH parameter using #define directive. This parameter will set clock stretch factor. Its default value is 1. I2C statements can not be used in subroutines, also. Here is one combined example:
Example 1:
#define I2C_SDA_REG = PORTB
#define I2C_SDA_BIT = 0
#define I2C_SCL_REG = PORTB
#define I2C_SCL_BIT = 1
#define SEROUT_REG = PORTB
#define SEROUT_BIT = 3
#define SEROUT_BAUD = 9600
Dim addr As Word
Dim data As Byte
Dim x As Byte
Dim x1 As Byte
Dim x2 As Byte
Dim x3 As Byte
WaitMs 1000
For addr = 0 To 31
data = 255 - addr
I2CWrite 0xa0, addr, data
Gosub setx1x2x3
Serout "Write To EEPROM: ", x1, x2, x3, CrLf
WaitMs 1000
Next addr
For addr = 0 To 31
I2CRead 0xa0, addr, data
Gosub setx1x2x3
Serout "Read From EEPROM: ", x1, x2, x3, CrLf
WaitMs 1000
Next addr
End
setx1x2x3:
x1 = data / 100
x = data Mod 100
x2 = x / 10
x3 = x Mod 10
x1 = x1 + 48
x2 = x2 + 48
x3 = x3 + 48
Return
There is a set of low-level I2C communication statements available, if it is needed to have more control over I2C communication process. I2CPREPARE statement with no arguments will prepare SDA and SCL lines for I2C communication. I2CSTART statement will generate start condition, and I2CSTOP statement will generate stop condition. One byte can be sent to the I2C slave using I2CSEND statement. After the statement is executed C bit in STATUS register will hold the copy of the state on the SDA line during the acknowledge cycle. There are two statements that can be used to receive one byte from I2C slave. I2CRECA or I2CRECEIVEACK will generate acknowledge signal during acknowledge cycle after the byte is received. I2CRECN or I2CRECEIVENACK will not generate acknowledge signal during acknowledge cycle after the byte is received. One example:
Example 2:
#define I2C_SDA_REG = PORTB
#define I2C_SDA_BIT = 0
#define I2C_SCL_REG = PORTB
#define I2C_SCL_BIT = 1
Dim addr As Word
Dim data1 As Byte
Dim data2 As Byte
Dim data3 As Byte
addr = 0
I2CPrepare
I2CStart
I2CSend 0xa0
I2CSend addr.HB
I2CSend addr.LB
I2CStop
I2CStart
I2CSend 0xa1
I2CReceiveAck data1
I2CReceiveAck data2
I2CRecN data3
I2CStop
• Support for software master SPI (Serial Peripheral Interface) communication
Prior to using SPI related statements, SPI interface should be set up using #define directives. There are eight available parameters to define the connection of SCK, SDI, SDO and (optionally) CS lines:
SPI_SCK_REG - defines the port where SCK line is connected to
SPI_SCK_BIT - defines the pin where SCK line is connected to
SPI_SDI_REG - defines the port where SDI line is connected to
SPI_SDI_BIT - defines the pin where SDI line is connected to
SPI_SDO_REG - defines the port where SDO line is connected to
SPI_SDO_BIT - defines the pin where SDO line is connected to
SPI_CS_REG - defines the port where CS line is connected to
SPI_CS_BIT - defines the pin where CS line is connected to
The assumed settings are active-high for Clock line and active-low for ChipSelect line. That can be changed by assigning the value 1 to SPICLOCK_INVERT and/or SPICS_INVERT parameters by #define directive. For slower SPI devices, it might be necessary to use longer clock pulses. The default clock stretch factor (0) can be changed by setting SPICLOCK_STRETCH parameter.
SPIPREPARE statement (no arguments) will prepare interface lines for SPI communication. SPICSON and SPICSOFF statements will enable/ disable the ChipSelect line of the interface. One byte can be sent to the SPI peripheral using SPISEND statement. To receive a byte from the peripheral SPIRECEIVE statement should be used. To send the specified number of bits there is SPISENDBITS statement available. Its first argument should be the number of bits to be sent [1-8] and the second argument is a byte variable or constant. SPI statements can not be used in subroutines. Here is one example for using 25C040 SPI eeprom:
Example 1:
#define SPI_CS_REG = PORTC
#define SPI_CS_BIT = 0
#define SPI_SCK_REG = PORTC
#define SPI_SCK_BIT = 1
#define SPI_SDI_REG = PORTC
#define SPI_SDI_BIT = 2
#define SPI_SDO_REG = PORTC
#define SPI_SDO_BIT = 3
SPIPrepare
Dim addr As Byte
Dim data As Byte
addr = 0x20
data = %10101010
SPICSOn
SPISend 0x06
SPICSOff
SPICSOn
SPISend 0x02
SPISend addr
SPISend data
SPICSOff
WaitMs 500
addr = 0x20
data = 0
SPICSOn
SPISend 0x03
SPISend addr
SPIReceive data
SPICSOff
Here is the same example written for 93C86 Microwire EEPROM:
Example 2:
#define SPICS_INVERT = 1
#define SPI_CS_REG = PORTC
#define SPI_CS_BIT = 0
#define SPI_SCK_REG = PORTC
#define SPI_SCK_BIT = 1
#define SPI_SDI_REG = PORTC
#define SPI_SDI_BIT = 2
#define SPI_SDO_REG = PORTC
#define SPI_SDO_BIT = 3
SPIPrepare
Dim addr As Byte
Dim data As Byte
SPICSOn
SPISendBits 6, %100110
SPISendBits 8, %00000000
SPICSOff
addr = 10
data = %00110011
SPICSOn
SPISendBits 6, %101000
SPISendBits 8, addr
SPISend data
SPICSOff
SPICSOn
SPISend 0x00
SPICSOff
WaitMs 500
addr = 10
data = 0
SPICSOn
SPISendBits 6, %110000
SPISendBits 8, addr
SPIReceive data
SPICSOff
• Interfacing Radio Control (R/C) servos
For writing applications to interface R/C servos there are two statements available: SERVOIN and SERVOOUT. R/C servo is controlled by a train of pulses (15-20 pulses per second) whose length define the position of the servo arm. The valid length of pulses is in the range 1-2ms. These two statements have two arguments. The first argument of both statements is the microcontroller pin where the servo signal is received or transmitted. For SERVOIN statement that pin should be previously setup as an input pin and for SERVOOUT statement the pin should be setup for output. The second argument of SERVOIN statement must be a Byte variable where the length of the pulse will be saved. The pulses are measured in 10us units, so it is possible to measure pulses in the range 0.01-2.55ms. The value stored in the variable for normal servos should be in the range 100-200. The second argument of the SERVOOUT statement should be a Byte variable or constant that determines the length of the generated pulse. For proper operation of the target servo SERVOOUT statement should be executed 15-20 times during one second. Here is an example of the servo reverse operation:
Dim length As Byte
TRISB.0 = 1
TRISB.1 = 0
loop:
ServoIn PORTB.0, length
If length < 100 Then length = 100
If length > 200 Then length = 200
length = length - 100
length = 100 - length
length = length + 100
ServoOut PORTB.1, length
Goto loop
• Interfacing Stepper Motors
Prior to using stepper motor related statements, its connection and desired drive mode should be set up using #define directives. There are eight available parameters to define the connection of A, B, C and D coils:
STEP_A_REG - defines the port where A coil is connected to
STEP_A_BIT - defines the pin where A coil is connected to
STEP_B_REG - defines the port where B coil is connected to
STEP_B_BIT - defines the pin where B coil is connected to
STEP_C_REG - defines the port where C coil is connected to
STEP_C_BIT - defines the pin where C coil is connected to
STEP_D_REG - defines the port where D coil is connected to
STEP_D_BIT - defines the pin where D coil is connected to
Coils A and C are actually parts of one single coil with common connection. The same is valid for B and D coil connections. There is also STEP_MODE parameter used to define the drive mode. If it is set to 1 (default) the motor will be driven in full-step mode. The value 2 should be used for half-step mode. The first basic statement that should be used is STEPHOLD. It will configure used pins as outputs and also energize A and B coils to fix the rotor in its initial position. For moving rotor in clockwise and counterclockwise directions there are STEPCW and STEPCCW statements available. Their first argument is the number of rotor steps that will be performed and it can be Byte data type constant or variable. The second argument defines the delay between consecutive steps expressed in microseconds by a Byte or Word data type variable or constant. If using STEPCW statement results in rotor movement in counterclockwise direction then connection settings for B and D coils should be exchanged. Here are two examples (the second example uses delays suitable for simulation in the simulator):
Example 1:
All_Digital
ADCON0.ANS0 = 1
#define STEP_A_REG = PORTC
#define STEP_A_BIT = 0
#define STEP_B_REG = PORTC
#define STEP_B_BIT = 1
#define STEP_C_REG = PORTC
#define STEP_C_BIT = 2
#define STEP_D_REG = PORTC
#define STEP_D_BIT = 3
#define STEP_MODE = 2
WaitMs 1000
StepHold
WaitMs 1000
Dim an2 As Word
loop:
ADC_Read 2, an2
an2 = an2 * 200
an2 = an2 + 2000
StepCW 1, an2
Goto loop
Example 2:
All_Digital
#define STEP_A_REG = PORTC
#define STEP_A_BIT = 0
#define STEP_B_REG = PORTC
#define STEP_B_BIT = 1
#define STEP_C_REG = PORTC
#define STEP_C_BIT = 2
#define STEP_D_REG = PORTC
#define STEP_D_BIT = 3
#define STEP_MODE = 2
WaitUs 300
StepHold
WaitUs 1000
loop:
StepCCW 16, 300
WaitUs 1000
StepCW 24, 300
WaitUs 1000
Goto loop
• #define directive parameters
Here is the list of all available parameters for the #define directive, along with their default values and allowed ranges of values:
CONFIG - (default value: selected in software, allowed range: 0-FFF)
CLOCK_FREQUENCY - (default value: selected in software, allowed range: 0.01-120MHz)
OSCCAL_VALUE - (default value: FE, allowed range: 0-FF, not available for all devices)
SIMULATION_WAITMS_VALUE - (default value: -1 (not used), allowed range: 0-10)
ADC_Sample_uS - (from ADC Module library, default value and allowed range shown in Libraries info panel)
ADC_Clk - (from ADC Module library, default value and allowed range shown in Libraries info panel)
SEROUT_DELAYUS - (default value: 1000, allowed range: 0-65535)
SEROUT_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
SEROUT_BIT - (default value: 0, allowed range: 0-7)
SEROUT_BAUD - (default value: 9600, allowed range: 100-60000)
SERIN_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
SERIN_BIT - (default value: 1, allowed range: 0-7)
SERIN_BAUD - (default value: 9600, allowed range: 100-60000)
SERIN_TIMEOUT_REG - (default value: 0, allowed values: 0, or available registers for the selected device)
SERIN_TIMEOUT_BIT - (default value: 0, allowed range: 0-7)
I2CREAD_DELAYUS - (default value: 0, allowed range: 0-65535)
I2CCLOCK_STRETCH - (default value: 1, allowed range: 1-50)
I2C_SDA_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
I2C_SDA_BIT - (default value: 0, allowed range: 0-7)
I2C_SCL_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
I2C_SCL_BIT - (default value: 1, allowed range: 0-7)
SPI_CS_REG - (default value: 0, allowed values: 0, or available PORT registers for the selected device)
SPI_CS_BIT - (default value: 0, allowed range: 0-7)
SPI_SCK_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
SPI_SCK_BIT - (default value: 0, allowed range: 0-7)
SPI_SDI_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
SPI_SDI_BIT - (default value: 1, allowed range: 0-7)
SPI_SDO_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
SPI_SDO_BIT - (default value: 2, allowed range: 0-7)
SPICLOCK_STRETCH - (default value: 0, allowed range: 0-50)
SPICLOCK_INVERT - (default value: 0, allowed values: 0,1)
SPICS_INVERT - (default value: 0, allowed values: 0,1)
COUNT_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
COUNT_BIT - (default value: 0, allowed range: 0-7)
COUNT_MODE - (default value: 1, allowed values: 1,2)
FREQOUT_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
FREQOUT_BIT - (default value: 0, allowed range: 0-7)
STEP_A_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
STEP_A_BIT - (default value: 0, allowed range: 0-7)
STEP_B_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
STEP_B_BIT - (default value: 1, allowed range: 0-7)
STEP_C_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
STEP_C_BIT - (default value: 2, allowed range: 0-7)
STEP_D_REG - (default value: PORTB, allowed values: available PORT registers for the selected device)
STEP_D_BIT - (default value: 3, allowed range: 0-7)
STEP_MODE - (default value: 1, allowed values: 1,2)
• Library support
The library support is a new way for the basic statements implementation.
This is indeed an advanced feature, however that is a way that can be used by both the author and the users to extend the compiler language from the external library files.
The information is loaded by the compiler from the external textual .lib files stored in the OshonSoft application data
folder.
Library files can be edited by Notepad, or any other plain text editor.
OshonSoft .lib files are well documented with comments covering all the currently available features of the library support compiler engine.
Detailed description of all basic elements, statements and functions, that is loaded from the library files is displayed on the Libraries Info Panel.
oshonsoftpic10.lib (advanced and concise library support engine overview):
//the concept is that one library group contains the implementation of one new language functionality - one or more related statements or functions
//library items contain different implementations of the same functionality for different groups of microcontrollers
//#lib_item_begin must be followed by #processor
//#statement_begin, #statement_type, #argument sequence must be fulfilled for proper library load
//#processor comma-separated list of processors, x can be used as a wild card character
//#processor can be used in multiple lines to quote all devices if needed
//#parameter is used to implement #define parameters needed for the statement implementation
//#parameter const, parameter_name, allowed_range, default_value
//#parameter symbol, parameter_name, type (pin, bit, byte, address of), system_bit or system_register
//'pin' type is used for the bits in the PORT registers
//'address of' type will implement a constant parameter
//#variable is used to declare global system variables
//#variable variable_name, type (byte, word)
//#statement_begin statement_name [argument1_name[, argument2_name[, ...]]]
//#statement_type type (procedure; inline; function, f_type (byte, word))
//#argument argument_name, type (const xx, byte, byte system xx, word, word system xx), passing_type
//the default type of the system variables can not be changed with #argument
//passing_type (byval, byval allowed_constant_range, byref, byrefout) for statement_type procedure and inline
//passing_type (byval, byval allowed_constant_range) for statement_type function
//allowed_constant_range can contain arithmetic expressions in brackets, like 0-[EEPROM_Num-1]
//used to define parameters and statements that are not available or not applicable for the current item devices
//#parameter n/a, parameter_name
//#statement n/a, statement_name
//used to define alternative names for the parameter and statement names defined in the library group
//#alias_for <library_defined_element_name>, <new_alternative_name>
//used to define code variations among processors when only one or more register names in the code should be replaced with their alternatives
//#alternate_reg_name <register_name_used_in_code>, <alternate_register_name>
//code section can contain both inline assembler and basic language lines of code
//symbol and const parameters can be used directly in the assembler lines of code
//calculate[] or calc[] macro is available for double precision calculations
//calculate[] must be used to enter the parameter value in the basic code, will be replaced with the value of the parameter
//calculate[] can be used to perform one arithmetic operation, will be replaced with the value of the result
//calc[] arithmetic operators: +, -, *, /, % (modulus or remainder operator), \ (division returning integer result)
//calc[] macros can be nested
//reg_addr[] macro will be replaced with the register address; if not found, -1 is returned
//Clock_Freq in MHz is available as an argument
//Flash_Num and EEPROM_Num (total number of memory locations) are available as arguments
//int_val() and abs_val() functions are available for getting integer or absolute values of the argument
//int_val() and abs_val() can be used on one or both arguments in calc[] expression, and/or on the whole expression
//#if [], #endif macro is available
//#if comparison operators: ==,<>,<,>,<=,>=
//#if [] macro can be used without #endif when followed by one operation in the same line
//for example: #if [libtemp3 > 0.5] #math libtemp2 = calc[libtemp2 + 1]
//for example: #if [reg_addr[ADCON0] >= 0] ADCON0.ANS0 = 0
//'const' statement argument type can be used to load the numeric value into one of the library engine temp variables libtemp0-libtemp99
//for const arguments only 'byval' and 'byval allowed_constant_range' passing types are allowed
//#math directive is used to assign numeric value or the result of calc[] expression to one libtempxx variable
//for example: #math libtemp8 = calc[calc[Clock_Freq * 1000000] / calc[calc[libtemp2 + 1] * 4]] //exact baud rate achieved
//#if macro also works with libtemp0-libtemp99 variables
//desired info can be inserted as a comment in the generated assembler source with the #echo directive
//#echo is followed by a string that can contain temp variable names to be replaced with their numeric values
//for example: #echo "exact baud rate achieved = libtemp8; bit period = libtemp7µs; baud rate error = libtemp6%"
//when calling 'procedure' type statements with one 'byval' byte argument declared, library engine will accept multiple comma-separated arguments
//for that purpose #statement_accept_multiple_arguments directive should be placed after #statement_type
//variables are passed byte by byte starting from the lowest byte; string constants can be used, along with symbolic constants Qt, CrLf, Cr, Lf
//when calling 'procedure' type statements with one 'byrefout' byte argument declared, library engine will also accept multiple comma-separated arguments
//variables are filled in byte by byte starting from the lowest byte
//be careful that basic statements like WaitUs also make use of the system registers and can alter those values
//when using inline assembler code, be careful that basic statements expect to be called with BANK zero selected
//#banksel register_name_or_address
//#banksel directive should be used before any register access with assembler code, in order to use the compiler internal memory banking optimizations
//special function registers are declared in basic code as byte variables; if needed, this byte variable type can be changed with #redim directive
//#redim register_name new_type (byte, word)
//#pagesel directive should be used before all the GOTO and CALL instructions in the code
//#pagesel will be used by the compiler to setup the appropriate page bits for the next GOTO or CALL instruction
//list of available compiler system byte registers (bank 0): R0L, R0H, R1L, R1H, R2L, R2H, R3L, R3H, R4L, R4H, R5L, R5H
//list of available compiler system word registers (bank 0): R0HL, R1HL, R2HL, R3HL, R4HL, R5HL