Similar to the use of any language which is not limited to books and magazines only, the Basic programming language is not closely related to any special type of computer, processor or operating system. It is a general-purpose language. This fact can make some problems as the Basic language slightly varies depending on its application (like different dialects of one language). So, we are not going to provide you with a detailed description of all the attributes of Basic in this book. Instead, we are going to describe a concrete application of the Basic programming language, i.e. Basic used by the mikroBasic PRO for PIC compiler.
The Basic programming language is a simple and easy to understand programming language. To use it correctly, it is sufficient to know just a few basic elements that every program consists of. These are:
Identifiers
Comments
Operators
Expressions
Instructions
Constants
Variables
Symbols
Directives
Labels
Procedures and functions
Modules
Here is an example of how you should not write a program. No comments are included, labels’ names are meaningless, code sections are not grouped… This program is going to work properly, but its purpose and way of execution will be only known to the programmer who has written it (at least for a day or two).
Figure below illustrates the structure of a simple program written in Basic, pointing out the parts it consists of. This is an example of how you should write a program. Differences are more than obvious...
PROGRAM STRUCTURE
Similar to other programming languages, Basic provides a set of strictly defined rules to be observed when writing programs. For a program to be written in Basic, it is necessary to install a software which provides the appropriate work environment and understands these rules on your PC... When you write a letter, you need a word processing program, don’t you? In this case, you need the mikroBasic PRO for PICcompiler.
Unlike most programs you have already got used to dealing with, the process of writing programs in the compiler doesn’t start by selecting the File>New option, but Project>New. Why is that? Well, you write a program in a document with the .mbas extension (mikroBasic). You diligently write, write, write... When you compile it into a HEX code, a new document with the .hex extension will be created. At the same time the compiler will automatically create several documents in addition to it. The purpose of these documents is not important at this point. Of course, there must be something to connect them all. You get it - we are talking about a project. The program you write is just a part of it.
Just to be sure that we are on the same page... From now on the word module refers to a document with the .mbas extension. The text it contains is referred to as a program. Every project written in the mikroBasic PRO for PIC compiler has the .mbppi extension (microBasicProject for PIC) and consists of at least one module (Main Module).
Every project in mikroBasic PRO for PIC requires a single main module. It is identified by the keyword program and instructs the compiler from where to start the process of compiling. When you successfully create an empty project in Project Wizard, the main module will be automatically displayed in the Code Editor window:
program MyProject ' The main module is called MyProject here
main: ' Main procedure
... '*
... '* Write program code here
... '*
end.
Nothing may precede the program keyword, except comments. As mentioned above, the project may also include other modules which, unlike the main one, start with the module keyword.
module MyModule ' Auxiliary module is called MyModule
... '*
... '* Implements
... '*
end.
To make the compiler familiar with all modules which belong to one project, it is necessary to specify them in the main module using theinclude keyword followed by a quoted module name. The extension of these files should not be included. Only one module per includeclause is allowed. The number of include clauses is not limited, but they all must be specified immediately after the program (main module) name. Here’s an example:
program MyProgram ' Start of program (main module named ‘MyProgram’)
' Other modules included are:
include "utils" ' Module "utils"
include "strings" ' Module "strings"
include "MyUnit" ' Module "MyUnit"
...
ORGANIZATION OF THE MAIN MODULE
Basically, the main module can be divided in two sections: declarations and program body. What is a declaration in programming? A declaration is a process of defining the properties of identifiers to be used in the program. Like most other programming languages, Basic also requires all identifiers to be declared prior to being used in the program. Otherwise, the compiler may not be able to interpret them correctly. This is how a declaration of a variable called distance looks like:
dim distance as float ' Declare variable distance
As can be seen, it is a floating point variable, i.e. a number with optional decimal places. Two other variables are declared and namedspeed and time. Now, they can be used in the program as follows:
This is an example of how to write the main module correctly:
ORGANIZATION OF OTHER MODULES
Other modules start with the module keyword. Every module consists of three sections: include, interface and implementation. Only the implementation section is obligatory. It starts with the implements keyword. Follow the example below:
IDENTIFIERS
Identifiers are arbitrary names assigned to the basic language objects such as constants, variables, functions, procedures etc. Somebody just came to an idea to use the word identifier instead of name. As simple as that. Here are a few rules to be observed when using identifiers:
Identifiers may contain all the letters of alphabet (both upper and lower case), digits (0-9) and the underscore character ( _ ).
The first character of an identifier must not be a digit.
No identifier may contain special characters such as ! [{ # $ % & etc.
Basic is not case-sensitive, which means that FIRST, first and First will be considered identical.
The ^ (caret) symbol is used to denote an exponentiation operator, the * (asterisk) symbol is used to denote multiplication, while other symbols have their usual meanings.
Keywords being already used by the compiler must not be used as identifiers The mikroBasic keywords are listed in the following table:
Abstract
And
Array
As
At
Asm
Assembler
Automated
Bdata
Begin
Bit
Case
Cdecl
Class
Code
Compact
Const
Constructor
Contains
Data
Default
Deprecated
Destructor
Dispid
Dispinterface
Div
Do
Downto
Dynamic
Else
End
Except
Export
Exports
External
Far
File
Finalization
Finally
For
Forward
Function
Goto
Idata
If
Ilevel
Implementation
In
Index
Inherited
Initialization
Inline
Interface
Io
Is
Label
Large
Library
Message
Mod
Name
Near
Nil
Not
Object
Of
On
Or
Org
Out
Overload
Override
Package
Packed
Pascal
Shr
Shl
Sfr
Set
Sbit
Safecall
Rx
Resourcestring
Requires
Repeat
Reintroduce
Register
Record
Readonly
Read
Raise
Published
Public
Protected
Property
Program
Procedure
Private
Platform
Pdata
Small
Stdcall
Stored
String
Stringresource
Then
Threadvar
To
Try
Type
Unit
Until
Uses
Var
Virtual
Volatile
While
With
Write
Writeonly
Xdata
Xor
A list of identifiers which must not be used in the program.
COMMENTS
Comments are parts of the program used to provide more information about the program and make it clear to the user. In Basic, any text following a single quotation mark (') is considered a comment. Comments are not compiled into executable code. The compiler is capable of recognizing special characters used to mark where comments start and completely ignores the following text during compilation. Even though comments cannot affect the program execution, they are as important as any other part of the program because almost every program needs to be improved, modified, upgraded or simplified at some point. Without comments, it is almost impossible to understand even the simplest programs.
LABELS
Labels provide the easiest way of controlling the program flow. They are used to mark particular lines in the program where jump instruction and appropriate subroutine are to be executed. All labels must be terminated by ‘:’ so that the compiler can easily recognize them.
CONSTANTS
A constant is a number or a character the value of which cannot be changed during the program execution. Unlike variables, constants are stored in ROM memory of the microcontroller in order to save as much memory space of RAM as possible. The compiler recognizes constants by their names and prefix const. Every constant is declared under unique name which must be a valid identifier. Constants are available in decimal, hexadecimal and binary formats. The compiler distinguishes between them according to their prefixes. If a constant has no prefix, it is considered decimal by default.
FORMAT
PREFIX
EXAMPLE
Decimal
const MAX = 100
Hexadecimal
0x or $
const MAX = 0xFF
Binary
Floating point
const MAX = %11011101
Constants are declared in the declaration part of the program or routine. The syntax of constants is:
const constant_name [as type] = value
Constant names are usually written in capitals. The type of a constant is automatically recognized by its size. In the following example, the constant MINIMUM is considered a signed integer and will be stored within two bytes of Flash memory (16 bits):
const MINIMUM = -1000 ' Declare constant MINIMUM
Type of constant is optionally specified. In the absence of type, the compiler assumes the ‘smallest’ type that can accommodate the constant value.
const MAX as longint = 10000
const MIN = 1000 ' Compiler will assume word type
const SWITCH = "n" ' Compiler will assume char type
In the following example, a constant named T_MAX is declared so as to have a fractional value 32.60. Now, the program can compare the measured temperature to that constant with a meaningful name instead to number 32.60.
const T_MAX = 32.60 ' Declare temperature T_MAX
const T_MAX = 3.260E1 ' Another way of declaring constant T_MAX
A string constant consists of a sequence of characters. They are enclosed within double quotation marks. A blank space may also be included in the string constant as a character. String constants are used to represent non-numeric quantities such as names, addresses, messages etc.
const Message_1 = "Press the START button" ' Message 1 for LCD
const Message_2 = "Press the RIGHT button" ' Message 2 for LCD
const Message_3 = "Press the LEFT button" ' Message 3 for LCD
In this example, sending the Message_1 constant to an LCD will cause the message ‘press the START button’ to be displayed.
VARIABLES
A variable is a named object able to contain a data which can be modified during program execution. Every variable is declared under a unique name which must be a valid identifier. For example, to add two numbers (number1 + number2) in the program, it is necessary to have a variable to represent what we in everyday life call the sum. In this case number1, number and sum are variables. The syntax of one single variable declaration is as follows:
dim variable_name as type
Variables in Basic are typed, which means that it is necessary to specify the type of data a variable is to receive. Variables are stored in RAM and the memory space occupied (in bytes) depends on their type. In addition to single declarations, variables of the same type can be declared as a list. Here, identifier_list is a comma-delimited list of valid identifiers, whereas type can be any data type.
dim i, j, k as byte 'Define variables i, j, k
dim counter, temp as word 'Define variables counter and temp
SYMBOLS
Symbols in Basic allow you to create simple macros without parameters. It means that any code line may be replaced with one single identifier. Symbols, when used properly, can increase code legibility and reusability.
Symbols are declared at the beginning of the module, right after the module name and optional include directive. The scope of a symbol is always limited to the module in which it has been declared.
symbol symbol_name = code
Here, symbol_name must be a valid identifier to be used throughout the code. The code specifier can be any code line (literals, assignments, function calls, etc).
symbol MAXALLOWED = 216 ' Symbol MAXALLOWED for numeric value
symbol OUT = PORTA ' Symbol OUT for SFR
symbol MYDELAY = Delay_ms(762) ' Symbol MYDELAY for procedure call
dim cnt as byte ' Variable cnt
main:
if cnt > MAXALLOWED then ' Program checks whether cnt > 216
cnt = 0 ' If yes,
OUT.1 = 0 ' the following three commands
MYDELAY ' are to be executed
end if
... ' If not, program continues here
No RAM memory space is used for storing symbols being used in the program. The compiler will simply replace all symbols with appropriate code lines assigned to them when declared.
BASIC LANGUAGE DATA TYPES
There are several data types that can be used in the Basic programming language. Table below shows the range of values these data may have when used in their basic form.
DATA TYPE
DESCRIPTION
SIZE (NUMBER OF BITS)
RANGE OF VALUES
bit
One bit
1
0 or 1
sbit
One bit
1
0 or 1
byte, char
Character
8
0 ... 255
short
Signed short integer
8
-127 ... 128
word
Unsigned integer
16
0 ... 65535
integer
Signed integer
16
-32768 ... 32767
longword
32-bit word
32
0 ... 4294967295
longint
32-bit signed word
32
-2147483648 ... 2147483647
float
Floating point
32
±1.17549435082*10-38 ... ±6.80564774407*1038
AUTOMATIC DATA TYPE CONVERSION
The compiler automatically performs implicit conversion in the following situations:
if a statement requires an expression of particular type, but expression of different type is used;
if an operator requires an operand of particular type, but operand of different type is used;
if a function requires a formal parameter of particular type, but is assigned an object of different type; and
if a function result does not match the declared function return data type.
PROMOTION
When operands are of different types, implicit conversion promotes a less complex to a more complex type as follows:
bit → byte
short, byte/char → integer, word, longint, longword
integer, word → longint, longword
short, byte/char, integer, word, longint, longword → float
DATA CLIPPING
In assignment statements and statements requiring an expression of particular type, the correct value will be stored in destination only if the result of expression doesn’t exceed the destination range. Otherwise, if expression evaluates to a more complex type than expected, the excess data will simply be clipped, i.e. higher bytes will be lost.
dim i as byte ' Variable i occupies one byte of RAM
dim j as word ' Variable j occupies two bytes of RAM
...
j = $FF0F
i = j ' i becomes $0F, higher byte $FF is lost
EXPLICIT DATA TYPE CONVERSION
Explicit conversion may be executed upon any expression at any point by writing desired type keyword (byte, word, short, integer, longint, float...) before the expression to be converted. The expression must be enclosed in parentheses. Explicit conversion cannot be performed upon the operand to the left of the assignment operator.
a = word(b) ' Explicit conversion of expression b
word(b) = a ' Compiler will report an error
A special case of explicit conversion is a conversion between signed and unsigned data types as it does not affect the binary representation of data.
dim a as byte
dim b as short
'...
b = -1
a = byte(b) ' a is 255, not -1
' Data doesn’t change its binary representation %11111111
' it is just interpreted differently by the compiler
OPERATORS
An operator is a symbol used to denote particular arithmetic, logic or some other operation. Every operation is performed upon one or more operands (variables or constants) in an expression. Besides, every operator features priority execution and associativity. If an expression contains more than one operand, the order of their execution is determined by the level of their priority. There are four priority categories in Basic. Operators belonging to the same category have equal priority. If two or more operators have the same priority level, the operations are performed from left to right. Parenthesis can be used to define the priority of the operation within an expression. Each category is assigned either left-to-right or rightto- left associativity rule. Refer to the table below.
PRIORITY
OPERATORS
ASSOCIATIVITY
High
@ not + -
from right to left
* / div mod and << >>
from left to right
+ - or xor
from left to right
Low
= <> < > <= >=
from left to right
ARITHMETIC OPERATORS
Arithmetic operators are used to perform arithmetic operations. These operations are performed upon numeric operands and always return numerical results. Binary operations are performed upon two operands, whereas unary operations are performed upon one operand. All arithmetic operators associate from left to right.
OPERATOR
OPERATION
+
Addition
-
Subtraction
*
Multiplication
/
Division - floating point
div
Division - round down
mod
Reminder
DIVISION BY ZERO
If a zero (0) is used explicitly as the second operand in the division operation (x div 0), the compiler will report an error and will not generate a code. In case of implicit division where the second operand is an object the value of which is 0 (x div y, where y=0), the result will be undefined.
RELATIONAL OPERATORS
Relational operators are used to compare two variables and determine the validity of their relationship. In mikroBasic, all relational operators return 255 if the expression is true, or zero (0) if it is false. The same applies in expressions such as ‘if the expression is true then...’
OPERATOR
MEANING
EXAMPLE
TRUTH CONDITION
>
is greater than
b > a
if b is greater than a
>=
is greater than or equal to
a >= 5
If a is greater than or equal to 5
<
is less than
a < b
if a Is less than b
<=
is less than or equal to
a <= b
if a Is less than or equal to b
=
is equal to
a = 6
if a Is equal to 6
<>
is not equal to
a <> b
if a Is not equal to b
LOGIC BITWISE OPERATORS
Logic bitwise operators are performed upon bits of an operand. They associate from left to right. The only exception is the bitwise complement operator not which associates from right to left. Bitwise operators are listed in the table on the right:
The bitwise operators and, or and xor perform logic operations upon appropriate pairs of bits of operands. The not operator complements each bit of one single operand.
OPERAND
MEANING
EXAMPLE
RESULT
<<
Shift left
A = B << 2
B = 11110011
A = 11001100
>>
Shift right
A = B >> 3
B = 11110011
A = 00011110
and
Bitwise AND
C = A and B
A=11100011
B=11001100
C = 11000000
or
Bitwise OR
C = A or B
A=11100011
B=11001100
C = 11101111
not
Bitwise NOT
A = not B
B = 11001100
A = 00110011
xor
Bitwise EXOR
C = A xor B
A = 11100011
B = 11001100
C = 00101111
The bitwise operators and, or and xor perform logic operations upon appropriate pairs of bits of operands. The not operator complements each bit of one single operand.
$1234 and $5678 ' result is $1230 because:
' $1234 : 0001 0010 0011 0100
' $5678 : 0101 0110 0111 1000
' ----------------------------
' and : 0001 0010 0011 0000 ... that is, $1230
$1234 or $5678 ' equals $567C
$1234 xor $5678 ' equals $444C
not $1234 ' equals $EDCB
BITWISE SHIFT OPERATORS
There are two shift operators in mikroBasic. These are the << operator which moves bits to the left and the >> operator which moves bits to the right. Both operators have two operands each. The left operand is an object to move, whereas the right operand is a number of positions to move the object by. Both operands must be of integral type. The right operand must be a positive value.
By shifting an operand left (<<), the leftmost bits are discarded, whereas ‘new’ bits on the right are assigned zeroes. Shifting unsigned operand to the left by n positions is equivalent to multiplying it with 2n. The same applies to signed operands if all discarded bits are equal to the sign bit.
dim num as word ' declare variable num as word
num = 1 ' asign it decimal value 1 (00000000 00000001 bin.)
num << 5 ' equals 32 (00000000 00100000 bin.)
By shifting operand right (>>), the rightmost bits are discarded, whereas ‘new’ bits on the left are assigned zeroes (in case of unsigned operand) or the sign bit (in case of signed operand). Shifting operand to the right by n positions is equivalent to dividing it by 2n.
dim num as integer ' declare variable num as signed integer
num = 0xFF56 ' asign it hex value FF56 (11111111 01010110 bin.)
num >> 4 ' equals 0xFFF5 (11111111 11110101 bin.)
CONDITIONAL STATEMENTS
Conditions are common ingredients of a program. They enable one or a number of statements to be executed depending on the validity of an expression. In other words ‘If the condition is met (...), do (...). Otherwise, do (...)’. A conditional statement can be followed either by a single statement or by a block of statements to execute.
CONDITIONAL STATEMENT IF
The syntax of a simple form of the if statement is:
if expression then
operations
end if
If the result of expression is true (not 0), operations are performed, then the program proceeds with execution. If the result of expression is false (0), operations are not performed and the program immediately proceeds with execution.
The if operator can also be used in combination with else operators:
if expression then
operations1
else
other operations2
end if
If the result of expression is true (not 0), operations1 are performed, otherwise operations2 are performed. The program proceeds with execution after these operations are performed.
NESTED IF STATEMENTS
Nested if statement need additional attention. A nested if-statement is a statement used inside the other if-statement. As a rule, they are parsed starting from the most nested ifstatement, whereas each else statement is bound to the nearest available if on its left:
SELECT CASE STATEMENT
The select case statement is a conditional statement with multiple branching. It consists of a selector expression (condition) and a list of possible values of that expression. The syntax of the select case statement is:
The selector specifier is an expression which should evaluate as integral value.
Specifiers value_1...value_n represent selector’s possible values and can be literals, constants or constant expressions. Specifiers statements_1 ...statements_n can be any statements.
The case else clause is optional.
First, the selector expression is evaluated. It is then compared to all available values. If the match is found, the statements following the match evaluate and the select case statement terminates. If there are multiple matches, statements following the first match will be executed. If none of the values matches the selector, then default_statements in the case else clause (if there is one) are executed.
Here is an example of the select case statement:
select case decimal_digit ' Decimal-digit value is being checked
case 0
mask = %01111110 ' Display "0"
case 1
mask = %00110000 ' Display "1"
case 2
mask = %01101101
case 3
mask = %01111001
case 4
mask = %00110011
case 5
mask = %01011011
case 6
mask = %01011111
case 7
mask = %01110000
case 8
mask = %01111111
case 9
mask = %01111011
end select
This program routine converts decimal digits into appropriate binary combination on the port in order to display them on an LED display.
PROGRAM LOOPS
Some instructions (operations) have to be executed more than once in the program. A set of commands being repeated makes a program loop. How many times it will be executed, i.e. how long the program will stay within a loop, depends on the conditions to leave the loop.
WHILE LOOP
The while loop is implemented when the number of iterations is not specified. It is necessary to check the iteration condition before a loop execution. Simply put, the while loop is executed while all necessary conditions for its execution are met... The syntax of the while loop looks as follows:
while expression
statements
wend
The statements specifier represents a group of statements which are executed repeatedly as long as the value of the expression specifier which represents an expression is true. In other words, the program remains in the loop until expression becomes false. The value of expression is checked before the next iteration is executed. Accordingly, if it is false before entering the loop, no iterations executes, i.e. statements will never be executed. The program will proceed with execution from the end of the while loop (from instructions following the wend instruction).
A special type of the program loop is an endless loop. It is created if the condition to exit loop remains unchanged within the loop.
In this case, the execution is simple as the result in brackets is always true (1 will allways be different from 0), which means that the program remains in the loop.
while 1 ' ‘true’ can be written instead of ‘1’
... ' Expressions will be unceasingly executed (endless loop)
...
wend
FOR LOOP
The for loop is implemented when the number of iterations is specified. The syntax of the for loop looks as follows:
for counter = initial_value to final_value [step step_value]
statements
next counter
Here, with each iteration of the loop, the counter variable is incremented by step_value. The step_value parameter is an optional integer value, considered 1 if omitted. Before the first iteration, the counter (counter) is set to its initial value (initial_value) and will be incremented until it reaches or exceeds the final value (final_value). Statements will be executed with each iteration. Iinitial_value and final_value should be expressions compatible with the counter, whereas the statements specifier can be any statement that doesn’t change the counter value. Note that the step_value parameter may be negative, thus enabling a countdown.
for k=1 to 5 ' Increase variable k five times (from 1 to 5) and
operation ' keep on executing "operation" every time
...
next k
A set of instructions (operation) will be executed five times. After that, it will be determined that the k<5 is false (after 5 iterations k=5) and the program will exit the for loop.
DO LOOP
The do loop is implemented when the number of iterations is not specified. The loop is executed repeatedly until the expression evaluates to true. The syntax of the do loop is:
do
statements
loop until expression
In this case, the statements specifier represents a group of statements which are executed as long as the expression (expression) is true. The loop conditions are checked at the end of the loop, so the loop is executed at least once regardless of whether the condition is true or false. In the following example, the program remains in the do loop until variable a reaches 1E06 (a million iterations).
a = 0 ' Set initial value
do
a = a+1 ' Operation in progress
loop until a <= 1E06 ' Check condition
WRITING CODE IN ASSEMBLY LANGUAGE
Sometimes a program in Basic requires parts of the code to be written in assembly language. In this way some parts of the program can be executed in a precisely defined way for exact period of time. For example, when it is necessary to provide very short pulses (a few microseconds) to appear periodically on a microcontroller pin, the best solution is to write an assembly code for pulse duration control. The asm command is used to introduce one or more assembly instructions to the program written in Basic:
asm
Assembly language instructions
...
end asm
Assembly instructions may use objects (constants, variables, routines etc.) that must be previously declared in the Basic language. It goes without saying that these objects are declared according to the rules of the Basic language. Refer to the example below:
ARRAYS
An array is a finite and arranged list of variables of the same type called elements. This type is called the base type. Each element is assigned a unique index so that different elements may have the same value. An array is declared by specifying the type of its elements (called array type), its name and the number of its elements enclosed within brackets:
dim array_name as component_type [number_of_components]
Elements of an array are identified by their position. Indices go from 0 (the first element of an array) to N-1 (N is the number of elements contained in an array). The compiler must know how many memory locations to allocate when an array is declared and because of that the array size can’t be variable.
ELEMENTS OF ARRAY
CONTENTS OF ELEMENT
shelf[0]
7
shelf[1]
23
shelf[2]
34
shelf[3]
0
shelf[4]
0
shelf[5]
12
shelf[6]
9
...
...
...
...
shelf [99]
23
To illustrate it, an array can be thought of as a shorter or longer list of variables of the same type where each of these is assigned an ordinal number always starting from zero. Such an array is called a vector. Table on the right shows an array named shelf which consists of 100 elements.
In this case, the contents of a variable (element) represents a number of products the shelf contains. Elements are accessed by indexing, i.e. by specifying their indices enclosed in square brackets:
dim shelf as byte [100] ' Declare the array "shelf" with 100 elements
shelf [4] = 12 ' 12 items are ‘placed’ on shelf [4]
temp = shelf [1] ' Variable shelf [1] is copied to
' variable temp
In constant arrays, elements can be assigned their contents during array declaration. In the following example, an constant array named CALENDAR is declared and each element is assigned specific number of days:
const CALENDAR as byte [12]= (31,28,31,30,31,30,31,31,30,31,30,31)
The number of assigned values must not exceed the specified array length, but can be less. In this case, the trailing ‘excess’ elements will be assigned zeroes.
GOTO STATEMENT
The goto statement enables you to make an absolute jump to another point in the program. Be careful when using this statement since its execution causes an unconditional jump ignoring any type of nesting limitations. The destination point is identified by a label, which is used as an argument for the goto statement. A label consists of a valid identifier followed by a colon (:).The syntax of the goto statement is:
goto: label_name
This statement executes a jump to the label_name specifier which represents a label. The goto statement can precede or follow the label. Hence it is not possible to jump into or out of a procedure or function. The goto statement can be used to break out from any level of nested structures. It is not advisable to jump into a loop or other structured statement as it may give unexpected results.
GOSUB STATEMENT
A subroutine is a portion of code within a larger program executed upon demand. It performs a specific task and is relatively independent from the rest of code. The interpreter will jump to the subroutine, execute it, and return to the main program. Keywords gosub and return are used in the Basic language to denote start and end of subroutine.
Subroutines are considered by many to be hard to maintain, difficult to read and digest, just like the goto statement. Use them just if you don’t have any better solution.
ACCESSING INDIVIDUAL BITS
Compiler mikroBasic PRO for PIC, installed on your PC, includes a list of supported PIC microcontrollers with all registers, their accurate addresses and bit names. The compiler allows you to access individual bits of these registers by their names, without specifying their positions (the compiler already ‘knows’ them). There are a number of ways to access and modify one individual bit within a register. Let’s access the GIE bit (Global Interrupt Enable bit) for example. It’s the seventh bit of the INTCON register. One way to access this bit by its name is to write the following:
INTCON.GIE = 0 ' Clear Global Interrupt Enable Bit (GIE)
Instead of a bit name, a variable, constant, function call or an expression enclosed within parentheses may be used to denote the position of bit in a register. In addition, for individual bit access there are predefined global constants B0, B1, … , B7, or 0, 1, … 7, where 7 is considered the most significant bit.
INTCON.B0 = 0 ' Clear bit 0 of the INTCON register
ADCON0.5 = 1 ' Set bit 5 of the ADCON0 register
i = 5
STATUS.(i+1) = 1 ' Set bit 6 of the STATUS register
Finally, a desired bit may be accessed by using its alias name. In this case it’s the GIE_bit:
GIE_bit = 1 ' Set Global Interrupt Enable Bit (GIE)
SBIT TYPE
The mikroBasic PRO for PIC compiler has the sbit data type. This is the shortest data type referring to one single bit. If type sbit is assigned to a variable, the appropriate bit of some register will be changed by changing that variable without specyfing the register name and location. The sbit variable will behave like a pointer. In order to declare the sbit variable, it is sufficient to write:
dim Bit_name as sbit at Register_name.Bit_position
program MyProgram ' Main module
...
dim Output1 as sbit at PORTB.0 ' Variable Output1 is of sbit type
...
Output1 = 1 ' Pin PORTB.0 is set (5V)
BIT TYPE
The mikroBasic PRO for PIC compiler provides the bit data type that may be used for variable declarations.
dim bf as bit
Unlike variables of sbit type, only the bit name is declared here, whereas the compiler stores bit-variable into some of the free registers of RAM. As can be seen, it is not necessary to specify a bit of some specific register. The exact location of the variable of bit type is unknown to the user.
Bit and sbit types are implemented with the following limitations:
Cannot be used for argument lists and as function return values
Cannot be used as a member of structures
Cannot be used as array elements
Cannot be initialized
Cannot be pointed to
Their addresses cannot be red, therefore the unary operator @ cannot be used with variable of this type
dim ptr as ^bit ' invalid
dim arr as array[5] of bit ' invalid
PROCEDURES AND FUNCTIONS
Functions and procedures, collectively referred to as routines, are subprograms (selfcontained statement blocks) which perform a certain task based on a number of input parameters. Functions return a value after execution, while procedures don’t.
PROCEDURES
A procedure is a named block of code, i.e. a subroutine with some additional features. For example, it can accept parameters. Procedures are declared as follows:
sub procedure procedure_name(parameter_list)
[ local declarations ]
procedure body
end sub
The procedure_name specifier represents a procedure name and can be any valid identifier.
The parameter_list specifier within parentheses represents a list of formal parameters declared similar to variables. In mikroBasic PRO for PIC, parameters are passed to a procedure by value. To pass parameters by address, it is necessary to add the byref keyword at the beginning of the parameter declaration.
Local declarations are optional declarations of variables and constants which refer to the given procedure only.
Procedure body is a sequence of statements to be executed upon calling the procedure.
Procedures are called by their name followed by actual parameters placed in the same order as their matching formal parameters. Upon a procedure call, all formal parameters are created as local objects initialized by values of actual arguments.
'Add two numbers
sub procedure add (dim byref sum as word, dim x, y as byte)
sum = x + y ' add numbers x and y and store result into sum variable
end sub ' end of subprocedure
Now, we can call the add procedure to calculate full weight of a cargo for example:
add (gross_weight, net_weight, tare_weight)
FUNCTIONS
Functions must be properly declared in order to be correctly interpreted during the process of compiling.
sub function function_name(parameter_list) as return_type
[ local declarations ]
function body
end sub
Every declaration contains the following elements:
Function name is an identifier by which it will be possible to call a function (function_name in the example).
Type of result (returned value) is data type of the returned data (return_type in the example).
Declaration of parameters: each parameter consists of a variable, constant, pointer or array preceded by its data type specifier similar to any regular variable declaration (parameter_list in the example). They are used to pass information to the function when it is called.
Local declarations are optional declarations of variables and constants which refer only to the given function.
Function body is a sequence of statements to be executed upon calling the function.
Here is an example of how to define and use the power function:
'function which calculates xn based on input parameters x and n (n > 0)
sub function power(dim x, n as byte) as longint ' x and n are bytes, result is longint
dim i as byte ' i is a byte
result = 1 ' result = 1 if n = 0
if n > 0 then
for i = 1 to n
result = result*x
next i
end if
end sub
Now, we can call the power function to calculate 312 for example:
tmp = power(3, 12) ' Calculate 312
FUNCTION AND PROCEDURE LIBRARIES
Declarations of all functions and procedures being used in Basic are usually stored in special module files called libraries. Prior to using any of them in the program, it is necessary to specify the appropriate module by means of the include clause at the beginning of the program. It’s just a general rule. But if you write a program in compiler mikroBasic PRO for PIC it is sufficient to check desired library on the list and the appropriate module will be automatically included in the project. This compiler already has a number of such libraries. If the compiler encounters an unknown function or procedure during program execution, first it will look for its declaration in the previously checked libraries.
BUILT-IN ROUTINES IN MIKROBASIC PRO FOR PIC
In addition to function and procedure libraries, the mikroBasic PRO for PIC compiler provides a set of useful built-in functions:
Lo
Hi
Higher
Highest
Inc
Dec
Chr
Ord
SetBit
ClearBit
TestBit
Delay_us
Delay_ms
Vdelay_Advanded_ms
Vdelay_ms
Delay_Cyc
Clock_KHz
Clock_MHz
Reset
ClrWdt
DisableContextSaving
SetFuncCall
SetOrg
GetDateTime
GetVersion
The Delay_us and Delay_ms routines are generated in the place of call.
The Vdelay_ms, Delay_Cyc and Get_Fosc_kHz are actual Basic routines. Their sources can be found in the Delays.mbas file located in the uses folder of the compiler.
PREPROCESSOR
A preprocessor is an integral part of every compiler. Its function is to recognize and execute preprocessor instructions. What are preprocessor instructions? These are special instructions which do not belong to the Basic language, but are integrated into the compiler. Prior to compiling, the compiler activates the preprocessor which goes through the program in search for these instructions. If any found, the preprocessor will simply replace them by another text which, depending on the type of command, can be a file (command include) or just a short sequence of characters (command define). Then, the process of compiling may start. The preprocessor instructions can be anywhere in the source program and refer only to the part of the program following their appearance up to the end of the program.
PREPROCESSOR DIRECTIVE INCLUDE
Many programs often repeat the same set of commands for several times. In order to speed up the process of writing a program, these commands and declarations are usually grouped in particular modules that can easily be included in the program using the include directive. To be more precise, the include directive imports text from another document, no matter whether it is a set of commands, comments etc., into the program.
CONDITIONAL COMPILATION
Conditional compilation directives are typically used to make source programs easy to modify and compile for different microcontrollers. mikroBasic PRO for PIC supports conditional compilation. All conditional compilation directives must be completed within the module in which they have started.
DIRECTIVES #IF, #ELIF, #ELSE, AND #ENDIF
Conditional directives #if, #elif, #else and #endif are executed similar to the common Basic conditional statements. If an expression you write after #if has a non-zero value, then program lines following the #if directive will be treated as a valid program code and compiled into a hex code. The syntax thereof is:
#if constant_expression_1 'If constant_expression_1 is not zero,
<section_1> 'section_1 will be compiled
[#elif constant_expression_2 'If constant_expression_2 is not zero,
<section_2>] 'section_2 will be compiled
...
[#elif constant_expression_n 'If constant_expression_n is not zero,
<section_n>] 'section_n will be compiled
[#else 'If none of previous sections are compiled
<final_section>] 'final_section will be compiled
#endif 'End of #if directive
Each #if directive in a source file must be matched by a closing #endif directive. Any number of #elif directives can appear between #if and #endif directives, but only one #else directive is allowed. The #else directive, if present, must be the last directive before #endif.
Section can be any program code that may be recognized by the compiler or preprocessor. The preprocessor selects a single section by evaluating constant_expression following each #if or #elif directive until it finds a true (non-zero) constant_expression.
If all constant-expressions evaluate to false or no #elif directives appear, the preprocessor selects the final_section following the #else clause. If the #else clause is omitted and all instances of constant_expression in the #if block evaluate to false, no section will be selected for further processing.
Finally, the result is that only one code section, even the empty one, will be compiled.
POINTERS
As you know, every object in the program (variable, procedure, subroutine, etc.) is assigned one specific memory address. When declaring a variable in the program, the compiler automatically assigns it a free RAM location. During programming, these addresses are kept hidden from programmers. In other words - addresses are ‘secretly’ used... The possibility to access different objects by their names (identifiers) instead of addresses is one of the main advantages of the high-level programming languages. As a matter of fact it is much easier to deal with words (names) than with numbers. Besides, the compiler takes care of associating objects and their adresses. Addressing objects by specifying their names is called direct addressing.
However, sometimes you have to deal with memory location addresses. Then, pointers are used - variables holding memory address of an object. In this case it is possible to access objects using pointers only. This way of addressing is therefore called indirect addressing.
Prior to using a pointer, it is necessary to declare its data type. Simply, add a caret prefix (^) before the type.
dim pointer_p as ^word ' pointer_p points to data of word type
If it is required to store a variable at some specific RAM memory location, then the absolute directive should be used in the program as follows:
dim variable_a as word absolute 12 ' variable_a will occupy 1 word
' (16 bits) at address 12
Now, if you want to access data at the pointer’s memory location, you need to add a caret after the pointer's name. For example, let’s declare the above mentioned pointer pointer_p which points to a word (in this case, it is previously defined variable_a stored at address 12 in RAM). The pointed variable_a will be assigned value 26:
dim pointer_p as ^word 'Pointer_p points to data of word type
...
...
pointer_p = 12 'Pointer_p points to memory adress 12
...
pointer_p^ = 26 'Variable a at memory address 12 has value 26
'Similar to the absolute directive that is used for variables, the org
'directive specifies the starting address of a routine in ROM. It is
'appended to the routine declaration. For example:
sub procedure proc(dim par as word) org 0x200 ' Procedure will start at
... ' the address 0x200
end sub
In this case, the pointer_p pointer is assigned value 12 (pointer_p =12), which means that the memory address 12 is specified hereby.
If you want to change the value of a pointed variable, it is sufficient to change the pointer's value and add a caret symbol (^) as a suffix to it. Refer to figure on the right, variable variable_a at address 12 is assigned value 26 by means of the pointer_p pointer.
Pointers can point to data stored in any available memory space and can reside in any available memory space except in program memory space (ROM).
@ OPERATOR
The @ operator returns the address of an object, i.e. creates a pointer upon its operand. The following rules apply here:
If X is a variable, @X returns the address of X.
If F is a routine (function or procedure), @F creates a pointer to F.
dim temp as word
ptr_b as ^byte
ptr_arr as ^byte[10]
arr as byte[10]
main:
ptr_b = @arr ' @ operator will return ^byte
temp = @arr ' @ operator will return ^byte
ptr_arr = @arr ' @ operator will return ^byte[10]
end.
If variable X is of array type, the @ operator will return pointer to it's first basic element, except when the left side of the statement in which X is used is an array pointer. In this case, the @ operator will return pointer to array, not to it's first basic element.
Program structure in mikroBasic PRO for PIC:
Every program normally starts with a comment which provides information on the purpose of the program, date of program writing, programmer, version, changes made relative to the previous version etc. These comments (header) are not compulsory, but it’s a good habit to write them and have them in the program.
Every program code starts with a program directive followed by the program name.
If the program, apart from the main module, contains other modules as well, their names must be specified using the include directive (one include directive per each module). Accordingly, if the compiler, while compiling the main module, comes to an undeclared object (function, variable etc.), it will first try to find its declaration within declared modules. If no appropriate declaration is found, the compiler will report an error.
The include directive (if there is any) is followed by a code portion intended to declare variables, constants, procedures, subprograms, functions and other objects to be used later in the program. These declarations are used to reserve RAM registers for data storing as well as to instruct the compiler how to execute some function or procedure. For example, a data of byte type occupies only one register, while a data of float type occupies four registers.
The main program starts with the main: directive (always followed by a colon). It is also called a ‘program body’.
Every program is terminated with the end. directive (always followed by a period).