0% found this document useful (0 votes)
33 views

Pascal Syntax v101

Uploaded by

Filip Korać
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views

Pascal Syntax v101

Uploaded by

Filip Korać
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

DISCLAIMER:

All products are owned by MikroElektronika and protected by copyright law and interna-
tional copyright treaty. Therefore, you should treat this manual as any other copyright
material. It may not be copied, partially or as a whole without the written consent of
MikroElektronika. Manual PDF – edition can be printed for private or local use, but not
for distribution. Modifying manual is prohibited.

LICENSE AGREEMENT:
By using our products you agree to be bound by all terms of this agreement. Copyright
by MikroElektronika 2003 – 2008.
3

Lexical Elements Overview


The following text provides a formal definition of the mikroPascal lexical elements. It
describes different categories of word-like units (tokens) recognized by the mikroPascal.
During compilation, the source code file is parsed (broken down) into tokens and white-
space. The tokens in mikroPascal are derived from a series of operations performed on
your programs by the compiler.
A mikroPascal program starts as a sequence of ASCII characters representing the
source code, created by keystrokes using a suitable text editor (such as the mikroPascal
Code Editor). The basic program unit in mikroPascal is a file. It usually corresponds to a
named file located in RAM or on disk, having the extension .mpas.

Whitespace
Whitespace is the collective name given to spaces (blanks), horizontal and vertical tabs,
newline characters and comments. Whitespace can serve to indicate where tokens start
and end, but beyond this function, any surplus whitespace is discarded. For example,
two sequences

var i : char;
j : word;

and

var
i : char;

j : word;

are lexically equivalent and parse identically to give the nine tokens:

var
i
:
char
;
j
:
word
;
4

Whitespace in Strings
The ASCII characters representing whitespace can occur within string literals, thus being
protected from the normal parsing process (they remain part of the string). For example,

some_string := 'mikro foo';

parses to four tokens, including a single string literal token:

some_string
:=
'mikro foo'
;

Tokens

Token is the smallest element of a Pascal program that is meaningful to the compiler. The
parser separates tokens from the input stream by creating the longest token possible
using input characters in a left–to–right scan.

Literals
Literals are tokens representing fixed numeric or character values. The data type of a
constant is deduced by the compiler using such clues as numeric values and the format
used in the source code.

Integer Literals
Integral values can be represented in decimal, hexadecimal or binary notation.

 In decimal notation, numerals are represented as a sequence of digits (without com-


mas, spaces or dots), with optional prefix + or - operator to indicate the sign. Values
default to positive (6258 is equivalent to +6258);
 The dollar-sign prefix ($) or the prefix 0x indicates a hexadecimal numeral (for exam-
ple, $8F or 0x8F); and
 The percent-sign prefix (%) indicates a binary numeral (for example, %0101).

Here are some examples:


The allowed range of values is imposed by the largest data type in mikroPascal – long-
word.

11 // decimal literal
$11 // hex literal, equals decimal 17
0x11 // hex literal, equals decimal 17
%11 // binary literal, equals decimal 3

Compiler will report an error if the literal exceeds 0XFFFFFFFF.


5

Floating Point Literals


A floating-point value consists of:

 Decimal integer;
 Decimal point ;
 Decimal fraction; and
 e or E and a signed integer exponent (optional).

Negative floating constants are taken as positive constants with the unary operator minus
(-) prefixed.

mikroPascal limits floating-point constants to range ±1.17549435082 * 10- 38 ..


±6.80564774407 * 1038. Here are some examples:

0. // = 0.0
-1.23 // = -1.23
23.45e6 // = 23.45 * 10^6
2e-5 // = 2.0 * 10^-5
3E+10 // = 3.0 * 10^10
.09E34 // = 0.09 * 10^34

Character Literals
Character literal is one character from extended ASCII character set, enclosed with apos-
trophes. Character literal can be assigned to variables of byte and char type (variable of
byte will be assigned the ASCII value of the character). Also, you can assign a character
literal to a string variable.
Note: Quotes ("") have no special meaning in mikroPascal.

String Literals
String literal is a sequence of up to 255 characters from extended ASCII character set,
written in one line and enclosed with apostrophes. Whitespace is preserved in string lit-
erals, i.e. parser does not “go into” strings but treats them as single tokens.
The length of string literal is the number of characters it consists of. String is stored inter-
nally as a given sequence of characters plus a final null character (ASCII zero). This
appended “stamp” does not count against the string’s total length. String literal with noth-
ing in between the apostrophes (null string) is stored as a single null character.
You can assign string literal to a string variable or to an array of char.
Here are several string literals:

'Hello world!' // message, 12 chars long


'Temperature is stable' // message, 21 chars long
' ' // two spaces, 2 chars long
'C' // letter, 1 char long
'' // null string, 0 chars long
6

Keywords
Keywords are words reserved for special purpose, and cannot be used as normal iden-
tifier names. Apart from the standard Pascal keywords, all relevant SFRs are defined as
global variables and represent reserved words that cannot be redefined. Here is the
alphabetical listing of keywords in Pascal:

absolute except nil requires


abstract export nodefault safecall
and exports not sbit
array external object sealed
as far of set
asm file on shl
assembler final operator shr
at finalization or small
automated finally org stdcall
bdata for out stored
begin forward overload string
bit goto override threadvar
case helper package to
cdecl idata packed try
class if pascal type
code ilevel pdata unit
compact implementation platform until
const implements private uses
constructor in procedure var
contains index program virtual
data inherited property volatile
default initialization protected while
deprecated inline public with
destructor interface published write
dispid is raise writeonly
dispinterface label read xdata
div library readonly xor
do message record
downto mod register
dynamic name reintroduce
end near repeat
7

Identifiers
Identifiers are arbitrary names of any length given to functions, variables, symbolic con-
stants, user-defined data types and labels. Identifiers can contain the letters a to z and A to
Z, the underscore character “_” and digits 0 to 9. First character must be a letter or an under-
score. Pascal is not case sensitive, so that it considers Sum, sum, and suM equivalent iden-
tifiers. Although identifier names are arbitrary (within the rules stated), errors occur if the
same name is used for more than one identifier within the same scope.

Here are some valid identifiers:

temperature_V1
Pressure
no_hit
dat2string
SUM3
_vtext

Here are some invalid identifiers:

7temp // NO -- cannot begin with a numeral


%higher // NO -- cannot contain special characters
xor // NO -- cannot match reserved word
j23.07.04 // NO -- cannot contain special characters (dot)

Punctuators
The mikroPascal punctuators (also known as separators) are:

 [] - Brackets
 () - Parentheses
 , - Coma
 ; - Semicolon
 : - Colon
 . - Dot

Brackets
Brackets [ ] indicate single and multidimensional array subscripts:

var alphabet : array 1..30] of byte;


// ...
alphabet [3] := 'c';
8

Parentheses
Parentheses ( ) are used to group expressions, isolate conditional expressions and indi-
cate function calls and function declarations:

d := c * (a + b); // Override normal precedence


if (d = z) then ... // Useful with conditional statements
func(); // Function call, no arguments
function func2(n : word); // Function declaration w/ parameters

Comma
Comma (,) separates the arguments in function calls, identifiers in declarations and ele-
ments of array in initialization lists:

Lcd_Out(1, 1, txt);
var i, j, k: byte;
const MONTHS: array [1..12] of byte = (31,28,31,30,31,30,31,31,30,31,30,31);

Semicolon
Every statement in Pascal must be terminated by a semicolon. The only exception is the
last (outer most) end statement in program which is terminated by dot.

Colon
Colon (:) is used in declarations to separate identifier list from type identifier. For example:

var
i, j : byte;
k : word;

In the program, use the colon to indicate a labelled statement:

start: nop;
...
goto start;

Dot
Dot (.) indicates access to a field of a record. For example:

person.surname := 'Smith';

Dot is a necessary part of floating point literals. Also, dot can be used for accessing indi-
vidual bits of registers in mikroPascal.
9

Program Organization
Pascal imposes quite strict program organization. Below you can find models for writing
legible and organized source files. For more information on file inclusion and scope,
refer to Units, Scope and Visibility.

Organization of Main Unit


Basically, the main source file has two sections: declarations and program body.
Declarations should be properly placed in the code and organized in an orderly man-
ner. Otherwise, compiler may not be able to comprehend the program correctly. When
writing code, follow the model represented below. The main unit should be written as:

program { program name }


uses { include other units }

//********************************************************
//* Declarations (globals):
//********************************************************

{ constants declarations }
const ...

{ variables declarations }
var ...

{ labels declarations }
label ...

{ procedures declarations }
procedure procedure_name
{ local declarations }
begin
...
end;

{ functions declarations }
function function_name
{ local declarations }
begin
...
end;

//********************************************************
//* Program body:
//********************************************************

begin
{ write your code here }
end.
10

Organization of Other Units


Other units start with the keyword unit; implementation section starts with the keyword
implementation. Follow the model represented below:

unit { unit name }


uses { include other units }
//********************************************************
//* Interface (globals):
//********************************************************

{ constants declarations }
const ...

{ variables declarations }
var ...

{ procedures prototypes }
procedure procedure_name(...);

{ functions prototypes }
function function_name(...);

//********************************************************
//* Implementation:
//********************************************************

implementation
{ constants declarations }
const ...

{ variables declarations }
var ...

{ labels declarations }
label ...

{ procedures declarations }
procedure procedure_name
{ local declarations }
begin
...
end;

{ functions declarations }
function function_name
{ local declarations }
begin
...
end;

end.
11

Scope
The scope of identifier is a part of the program in which the identifier can be used to
access its object. There are different categories of scope depending on how and where
identifiers are declared:

Place of declaration Scope


Identifier is declared in the Scope extends from the point where it is declared to
declaration of a program, the end of the current block, including all blocks
function or procedure enclosed within that scope. Identifiers in the
outermost scope (file scope) of main unit are referred
to as globals, while other identifiers are locals.
Identifier is declared in the Scope extends the interface section of a unit from the
interface section of a unit point where it is declared to the end of the unit, and to
any other unit or program that uses that unit.
Identifier is declared in the Scope extends from the point where it is declared to
implementation section of the end of the unit. The identifier is available to any
a unit, but not within the function or procedure in the unit.
block of any function or
procedure

Visibility
The visibility of an identifier is the region of the program source code from which legal
access can be made to the identifier’s associated object.
Scope and visibility usually coincide, although there are circumstances under which an
object becomes temporarily hidden by the appearance of a duplicate identifier: the object
still exists but the original identifier cannot be used to access it until the scope of the
duplicate identifier is ended.
Technically, visibility cannot exceed scope, but scope can exceed visibility.

Units
In mikroPascal, each project consists of a single project file and one or more unit files.
Project file contains information about the project, while unit files, with extension .mpas,
contain the actual source code.

Units allow you to:

 break large programs into encapsulated parts that can be edited separately;
 create libraries that can be used in different projects; and
 distribute libraries to other developers without disclosing the source code.

Each unit is stored in its own file and compiled separately. Compiled units are linked to
create an application. To build a project, the compiler needs either a source file or a com-
piled unit file for each unit.
12

Uses Clause
mikroPascal includes units by means of the uses clause. It consists of the reserved word
uses, followed by one or more comma-delimited unit names, followed by a semicolon.
Extension of the file should not be included. There can be at most one uses clause in each
source file, and it must appear immediately after the program (or unit) name. Here’s an
example:

uses utils, strings, Unit2, MyUnit;

When encountering a given unit name, compiler will check for the presence of .mcl and
.mpas files, in the order specified by the search paths.

 If both .mpas and .mcl files are found, compiler will check their dates and include
newer one in the project. If the .mpas file is newer than the .mcl, a new library will be
recompiled over the old one;
 If only .mpas file is found, compiler will create the .mcl file and include it in the project;
 If only .mcl file is present, i.e. no source code is available, compiler will include it as found;
 If none found, compiler will issue a “File not found” warning.

Main Unit
Every project in mikroPascal requires a single main unit file. The main unit file is identi-
fied by the keyword program at the beginning; it instructs the compiler where to “start”.
After you have successfully created an empty project with Project Wizard, Code Editor
will display a new main unit. It contains the bare-bones of Pascal program:

program MyProject;

{ main procedure }
begin
{ Place program code here }
end.

Except for comments, nothing should precede the keyword program. After the program
name, you can optionally place the uses clause. Place all global declarations (constants,
variables, labels, routines) before the keyword begin.

Other Units
Other units start with the keyword unit. Newly created blank unit contains the bare-bones:

unit MyUnit;
implementation
end.

Except for comments, nothing should precede the keyword unit. After the unit name, you
can optionally place an uses clause.
13

Interface Section
Part of the unit above the keyword implementation is referred to as interface section. Here,
you can place global declarations (constants, variables, and labels) for the project. You can-
not define routines in the interface section. Instead, state the prototypes of routines (from
implementation section) that you want to be visible outside the unit. Prototypes must fully
match the declarations.

Implementation Section
Implementation section hides all the irrelevant innards from other units, allowing encapsulation of
code. Everything declared below the keyword implementation is private, i.e. has its scope limit-
ed to the file. When you declare an identifier in the implementation section of a unit, you cannot
use it outside the unit, but you can use it in any block or routine defined within the unit. By plac-
ing the prototype in the interface section of the unit (above the implementation) you can make the
routine public, i.e. visible outside of unit. Prototypes must fully match the declarations.

Variables
Variable is an object whose value can be changed during the runtime. Every variable is
declared under unique name which must be a valid identifier. Variables are declared in
the declaration part of the file or routine — each variable needs to be declared before it
can be used. Global variables (those that do not belong to any enclosing block) are
declared below the uses statement, above the keyword begin. Specifying a data type for
each variable is mandatory. Basic syntax for variable declaration is:

var identifier_list : type;

The identifier_list is a comma-delimited list of valid identifiers and type can be any data
type. Pascal allows shorthand syntax with only one keyword var followed by multiple vari-
able declarations. For example:

var i, j, k : byte;
counter, temp : word;
samples : array [100] of word;

Labels
Labels serve as targets for goto statements. Mark a desired statement with label like this:

label_identifier : statement

Before marking a statement, you must first declare the label. It is declared in declaration part of
unit or routine, similar to variables and constants. Declare labels using the keyword label:

label label1, ..., labeln;


14

Constants
Constant is a data whose value cannot be changed during the runtime. Using a constant
in a program consumes no RAM memory. Constants can be used in any expression, but
cannot be assigned a new value. Constants are declared in the declaration part of pro-
gram or routine. Declare a constant in the following way:

const constant_name [: type] = value;

Every constant is declared under unique constant_name which must be a valid identifi-
er. Constant names are normally written in uppercase. Constant requires you to specify
value, i.e. a literal appropriate for the given type. The type is optional; in the absence of
type, compiler assumes the “smallest” type that can accommodate value. Pascal allows
shorthand syntax with only one keyword const followed by multiple constant declarations.
Here’s an example:

const
MAX : longint = 10000;
MIN = 1000; // compiler will assume word type
SWITCH = 'n'; // compiler will assume char type
MSG = 'Hello'; // compiler will assume string type
MONTHS : array [1..12] of byte = (31,28,31,30,31,30,31,31,30,31,30,31);

Functions and Procedures


Functions and procedures, collectively referred to as routines, are subprograms which
perform a certain task based on a number of input parameters. Function returns a value
when executed, whereas procedure does not. mikroPascal does not support inline rou-
tines.

Functions
Function is declared as follows:

function function_name(parameter_list) : return_type;


{ local declarations }
begin
{ function body }
end;

The function_name represents a function’s name and can be any valid identifier. Within parenthe-
ses, parameter_list is a formal parameter list very similar to variable declaration. In Pascal, param-
eters are always passed to function by value — to pass the argument by address, add the key-
word var ahead of identifier. Local declarations are optional declarations of variables and/or con-
stants, local for the given function. Function body is a sequence of statements to be executed upon
calling the function.
15

Calling a function
A function is called by its name, with actual arguments placed in the same sequence as
their matching formal parameters. The compiler is able to coerce mismatching argu-
ments to the proper type according to implicit conversion rules. After function call, all for-
mal parameters are created as local objects initialized by values of actual arguments.
After return from a function, temporary object is created in the place of the call, and it is
initialized by the expression of return statement. This means that the function call as an
operand in complex expression is treated as the function result.
Here’s a simple function which calculates xn based on input parameters x and n (n > 0):

function power(x, n : byte) : longint;


var i : byte;
begin
i := 0; result := 1;
if n > 0 then
for i := 1 to n do result := result*x;
end;

Now we could call it to calculate, say 312:

tmp := power(3, 12);

Procedures
Procedure is declared as follows:

procedure procedure_name(parameter_list);
{ local declarations }
begin
{ procedure body }
end;

procedure_name represents a procedure’s name and can be any valid identifier. Within
parentheses, parameter_list is a formal parameter list very similar to variable declaration.
In Pascal, parameters are always passed to procedure by value — to pass the argument
by address, add the keyword var ahead of identifier. Local declarations are optional dec-
laration of variables and/or constants, local for the given procedure. Procedure body is a
sequence of statements to be executed upon calling the procedure.

Calling a procedure
A procedure is called by its name, with actual arguments placed in the same sequence
as their matching formal parameters. Upon procedure call, all formal parameters are cre-
ated as local objects initialized by values of actual arguments.
16

Types
Pascal is strictly typed language, which means that every variable and constant need to
have a strictly defined type, known at the time of compilation. The type serves:

 to determine the correct memory allocation required;


 to interpret the bit patterns found in the object during subsequent access; and
 in many type-checking situations, to ensure that illegal assignments are trapped.

mikroPascal supports many standard (predefined) and user-defined data types, includ-
ing signed and unsigned integers of various sizes, arrays, strings and pointers. Types
can be built from other types by the type declaration. For example:

Type MyType1 = array [10] of byte;


MyType2 = integer;
MyType3 = ^word;
MyType4 = ^MyType1;

var mynumber:MyType2;

Simple Types
Simple types represent types that cannot be divided into more basic elements, and are
the model for representing elementary data on machine level. Here is an overview of sim-
ple types in mikroPascal:

Type Size Range


byte 8–bit 0 – 255
char* 8–bit 0 – 255
word 16–bit 0 – 65535
short 8–bit -128 – 127
integer 16–bit -32768 – 32767
dword 32–bit 0 – 4294967295
longint 32–bit -2147483648 – 2147483647
-38 38
real 32–bit ±1.17549435082 * 10 .. ±6.80564774407 * 10
* char type can be treated as byte type in every aspect
17

Arrays
An array represents an indexed collection of elements of the same type (called the base
type). As each element has a unique index, arrays can meaningfully contain the same
value more than once.

Array Declaration
Array types are denoted by constructions of the form:

array [index_start .. index_end] of type

Each of the elements of an array is numbered from index_start through the index_end.
Specifier index_start can be omitted along with dots, in which case it defaults to zero.
Every element of an array is of type and can be accessed by specifying array name fol-
lowed by element’s index within brackets. Here are a few examples:

var
weekdays : array [1..7] of byte;
samples : array [50] of word;

begin
// Now we can access elements of array variables, for example:
samples [0] := 1;
if samples [37] = 0 then ...

Constant Arrays
Constant array is initialized by assigning it a comma-delimited sequence of values with-
in parentheses. For example:

// Declare a constant array which holds number of days in each month:


const MONTHS : array [1..12] of byte =
(31,28,31,30,31,30,31,31,30,31,30,31);
// Declare a constant numbers:
const NUMBERS : array [4] of array [4] of byte = ((0, 1, 2, 3),
(5, 6, 7, 8), (9, 10, 11,12), (13,14, 15, 16));

The number of assigned values must not exceed the specified length, but can be less
than the specified length, when the trailing “excess” elements are to be assigned zeroes.
18

Multi-dimensional Arrays
An array is one-dimensional if it is of scalar type. One-dimensional arrays are sometimes
referred to as vectors.
Multidimensional arrays are constructed by declaring arrays of array type. These arrays
are stored in memory in a way that the right most subscript changes fastest, i.e. arrays
are stored “in rows”. Here is a sample 2-dimensional array:

m: array [50] of array [20] of byte; //2-dimensional array of size 50x20

Variable m is an array of 50 elements, which in turn are arrays of 20 bytes each. Thus,
we have a matrix of 50x20 elements: the first element is m[0][0] and the last one is
m[49][19]. The first element of the 4th row would be m[0][4].
This is a commonly used technique when passing arrays as function parameters:

procedure example(var m: array [50] of array [20] of byte);


begin
...
inc(m [1][1]);
end;

var
m: array [50] of array [20] of byte; // 2-dimensional array of size 50x20
n: array [4] of array [2] of array [7] of byte; // 3-dimensional array of
size 4x2x7
begin
...
func(m);
end.

Strings
A string represents a sequence of characters and is equivalent to an array of char. It is
declared as:

string_name string [length]

The string_name needs to be a valid identifier. Specifier length is a number of characters the
string consists of. String is stored internally as the given sequence of characters plus a final
null character (zero). This appended “stamp” does not count against string’s total length.
The null string ('') is stored as a single null character.
19

You can assign string literals or other strings to string variables. String on the right side of an
assignment operator has to be the shorter one of two or of equal length. For example:

var
msg1 : string [20];
msg2 : string [19];
begin
msg1 := 'This is some message';
msg2 := 'Yet another message';
msg1 := msg2; // this is ok, but vice versa would be illegal

Alternately, you can handle strings element–by–element. For example:

var s : string [5];


//...
s := 'mik';
{
s [0] is char literal 'm'
s [1] is char literal 'i'
s [2] is char literal 'k'
s [3] is zero
s [4] is undefined
s [5] is undefined
}

String Splicing
mikroPascal allows you to splice strings by means of plus character. This kind of concatena-
tion is applicable to string variables/literals and character variables/literals. For control charac-
ters, use the non-quoted hash sign and a numeral (e.g. #13 for CR). Here is an example:

var msg : string [100];


res_txt : string [5];
res, channel : word;
begin
//...
// Get result of ADC
res := Adc_Read(channel);

// Create string out of numeric result


WordToStr(res, res_txt);
// Prepare message for output
msg := 'Result is' + // Text "Result is"
#13 + #10 + // Append CR/LF sequence
res_txt + // Result of ADC
'.'; // Append a dot

mikroPascal includes a String Library which automatizes string related tasks.


20

Pointers
A pointer is a data type which holds a memory address. While a variable accesses that
memory address directly, a pointer can be thought of as a reference to that memory
address. To declare a pointer data type, add a carat prefix (^) before type. For example,
if you are creating a pointer to an integer, you should write:

^integer;

To access the data at the pointer’s memory location, add a carat after the variable name.
For example, let’s declare variable p which points to a word, and then assign the point-
ed memory location value 5:

var p : ^word;
...
p^ := 5;

A pointer can be assigned to another pointer. However, note that only the address, not
the value, is copied. Once you modify the data located at one pointer, the other pointer,
when dereferenced, also yields modified data.

@ Operator
The @ operator returns the address of a variable or routine; that is, @ constructs a point-
er to its operand. The following rules apply to @:

 If X is a variable, @X returns the address of X.


 If F is a routine (a function or procedure), @F returns F’s entry point.

Records
A record (analogous to a structure in some languages) represents a heterogeneous set
of elements. Each element is called a field; the declaration of a record type specifies a
name and type for each field. The syntax of a record type declaration is

type recordTypeName = record


fieldList1 : type1;
...
fieldListn : typen;
end;

where recordTypeName is a valid identifier, each type denotes a type, and each fieldList
is a valid identifier or a comma-delimited list of identifiers. The scope of a field identifier
is limited to the record in which it occurs, so you don’t have to worry about naming con-
flicts between field identifiers and other variables. Note that in mikroPascal, you cannot
use the record construction directly in variable declarations, i.e. without type.
21

For example, the following declaration creates a record type called TDot:

type
TDot = record
x, y : real;
end;

Each TDot contains two fields: x and y coordinates; memory is allocated when you
instantiate the record, like this:

var m, n: TDot;

This variable declaration creates two instances of TDot, called m and n.

A field can be of previously defined record type. For example:

// Structure defining a circle:


type
TCircle = record
radius : real;
center : TDot;
end;

Accessing Fields
You can access the fields of a record by means of dot (.) as a direct field selector. If we
declare variables circle1 and circle2 of previously defined type TCircle:

var circle1, circle2 : TCircle;

We could access their individual fields like this:

circle1.radius := 3.7;
circle1.center.x := 0;
circle1.center.y := 0;

You cannot commit assignments between complex variables.


22

Types Conversions
Conversion of object of one type is changing it to the same object of another type (i.e.
applying another type to a given object). mikroPascal supports both implicit and explicit
conversions for built-in types.

Implicit Conversion
Compiler will provide an automatic implicit conversion in the following situations:
 statement requires an expression of particular type (according to language defini-
tion), and we use an expression of a different type;
 operator requires an operand of particular type and we use an operand of a different
type;
 function requires a formal parameter of particular type and we pass it an object of a
different type; and
 result does not match the declared function return type.

Promotion
When operands are of different types, implicit conversion promotes a less complex to
more complex type taking the following steps:

byte/char ĺ word
short ĺ integer
short ĺ longint
integer ĺ longint
integral ĺ real

Higher bytes of extended unsigned operand are filled with zeroes. Higher bytes of
extended signed operand are filled with bit sign (if number is negative, fill higher bytes
with one, otherwise with zeroes). For example:

var a : byte; b : word;


...
a := $FF;
b := a; // a is promoted to word, b becomes $00FF

Clipping
In assignments, and statements that require an expression of particular type, destination
will store the correct value only if it can properly represent the result of expression (that
is, if the result fits in destination range). If expression evaluates to a more complex type
than expected, excess data will be simply clipped (higher bytes are lost).

var i : byte; j : word;


...
j := $FF0F;
i := j; // i becomes $0F, higher byte $FF is lost
23

Explicit Conversion
Explicit conversion can be executed at any point in expression by inserting type keyword (byte,
word, short, integer, longint, or real) ahead of the expression to be converted. The expression
must be enclosed in parentheses. Explicit conversion can be performed only on the operand
left of the assignment operator. Special case is conversion between signed and unsigned
types. Explicit conversion between signed and unsigned data does not change binary repre-
sentation of data — it merely allows copying of source to destination. For example:

var a : byte; b : short;


...
b := -1;
a := byte(b); // a is 255, not 1
// This is because binary representation remains
// 11111111; it's just interpreted differently now

You can’t execute explicit conversion on the operand left of the assignment operator:

word(b) := a; // Compiler will report an error

Here is a conversion example:

var a, b, c : byte; cc : word;


...
a := 241;
b := 128;

c := a + b; // equals 113
c := word(a + b); // equals 369
cc := a + b; // equals 369

Arithmetic Conversions
When you use an arithmetic expression, such as a + b, where a and b are of different
arithmetic types, mikroPascal performs implicit type conversions before the expression is
evaluated. These standard conversions include promotions of “lower” types to “higher”
types in the interests of accuracy and consistency. Assigning a signed character object
(such as a variable) to an integral object results in automatic sign extension. Objects of
type short always use sign extension; objects of type byte always set the high byte to
zero when converted to int. Converting a longer integral type to a shorter type truncates
higher order bits and leaves low-order bits unchanged.
Converting a shorter integral type to a longer type either sign-extends or zero-fills the
extra bits of new value, depending on whether the shorter type is signed or unsigned,
respectively.

Note: Conversion of floating point data into integral value (in assignments or via explicit
typecast) produces correct results only if the float value does not exceed the scope of des-
tination integral type.
24

In details:
Here are the steps mikroPascal uses to convert operands in an arithmetic expression:

First, any small integral types can be converted according to the following rules:

1. byte converts to integer;


2. short converts to integer, with the same value;
3. short converts to integer, with the same value, sign-extended; and
4. byte converts to integer, with the same value, zero-filled.

The result of the expression is of the same type as that of the two operands.

Here are several examples of implicit conversion:

2 + 3.1 /*  2. + 3.1  5.1 */


5 / 4 * 3. /*  (5/4)*3.  1*3.  1.*3.  3. */
3. * 5 / 4 /*  (3.*5)/4  (3.*5.)/4  15./4  15./4.  3.75 */

Operators

Operators are tokens that trigger some computation when applied to variables and other
objects in an expression.
There are 4 precedence categories in mikroPascal. Operators in the same category have
equal precedence with each other. Each category has an associativity rule: left-to-right
(), or right-to-left (). In the absence of parentheses, these rules resolve the grouping
of expressions with operators of equal precedence.

Precedence Operands Operators Associativity


4 1 @ not + - ĸ
3 2 * / div mod and shl shr ĺ
2 2 + - or xor ĺ
1 2 = <> < > <= >= ĺ
25

Arithmetic Operators
Arithmetic operators are used to perform mathematical computations. They have numer-
ical operands and return numerical results. As char operators are technically bytes, they
can be also used as unsigned operands in arithmetic operations. All arithmetic operators
associate from left to right.

Operator Operation Operands Result


byte, short, byte, short,
integer, word, integer, word,
+ addition
longint, dword, longint, dword,
real real
byte, short, byte, short,
integer, word, integer, word,
- subtraction
longint, dword, longint, dword,
real real
byte, short, byte, short,
integer, word, integer, word,
* multiplication
longint, dword, longint, dword,
real real
byte, short, byte, short,
integer, word, integer, word,
/ division, floating-point
longint, dword, longint, dword,
real real
byte, short, byte, short,
division, rounds down to integer, word, integer, word,
div
nearest integer longint, dword, longint, dword,
real real
modulus, returns the
byte, short, byte, short,
remainder of integer division
mod integer, longint, integer, longint,
(cannot be used with floating
word, dword word, dword
points)

Division by Zero
If 0 (zero) is used explicitly as the second operand (i.e. x div 0), compiler will report an
error and will not generate code. But in the event of implicit division by zero : x div y,
where y is 0 (zero), result will be the maximum value for the appropriate type (for exam-
ple, if x and y are words, the result will be $FFFF).

Unary Arithmetic Operators


Operator - can be used as a prefix unary operator to change sign of a signed value.
Unary prefix operator + can be used, but it doesn’t affect the data.
For example:

b := -a;
26

Relational Operators
Use relational operators to test equality or inequality of expressions. All relational opera-
tors return TRUE or FALSE.
All relational operators associate from left to right.

Operator Operation
= equal
<> not equal
> greater than
< less than
>= greater than or equal
<= less than or equal

Relational Operators in Expressions


Precedence of arithmetic and relational operators was designated in such a way to allow
complex expressions without parentheses to have expected meaning:

a + 5 >= c - 1.0 / e //  (a + 5) >= (c - (1.0 / e))

Bitwise Operators
Use the bitwise operators to modify individual bits of numerical operands.

Bitwise operators associate from left to right. The only exception is the bitwise comple-
ment operator not which associates from right to left.

Bitwise Operators Overview

Operator Operation
bitwise AND; compares pairs of bits and generates an 1 result if
and
both bits are 1, otherwise it returns 0.
bitwise (inclusive) OR; compares pairs of bits and generates an 1
or
result if either or both bits are 1, otherwise it returns 0.
bitwise exclusive OR (XOR); compares pairs of bits and generates
xor
an 1 result if the bits are complementary, otherwise it returns 0.
not bitwise complement (unary); inverts each bit.
bitwise shift left; moves the bits to the left, it discards the far left bit
shl
and assigns 0 to the right most bit.
bitwise shift right; moves the bits to the right, discards the far right
shr bit and if unsigned assigns 0 to the left most bit, otherwise sign
extends.
27

Logical Operations on Bit Level


Bitwise operators and, or, and xor perform logical operations on appropriate pairs of bits
of their operands. Operator not complements each bit of its operand. For example:

$1234 and $5678 // equals $1230


{ because ..

$1234 : 0001 0010 0011 0100


$5678 : 0101 0110 0111 1000
----------------------------
and : 0001 0010 0011 0000 .. that is, $1230 }

Unsigned and Conversions


If number is converted from less complex to more complex data type, upper bytes are
filled with zeroes. If number is converted from more complex to less complex data type,
data is simply truncated (upper bytes are lost). For example:

var a : byte; b : word;


...
a := $AA;
b := $F0F0;
b := b and a;
{ a is extended with zeroes; b becomes $00A0 }

Signed and Conversions


If number is converted from less complex data type to more complex, upper bytes are
filled with ones if sign bit is 1 (number is negative); upper bytes are filled with zeroes if
sign bit is 0 (number is positive). If number is converted from more complex data type to
less complex, data is simply truncated (upper bytes are lost). For example:

var a : byte; b : word;


a := -12;
b := $70FF;
b := b and a;

{ a is sign extended, with the upper byte equal to $FF;


b becomes $70F4

Bitwise Shift Operators


Binary operators shl and shr move the bits of the left operand by a number of positions specified
by the right operand, to the left or right, respectively. Right operand has to be positive and less
than 255. With shift left (shl), left most bits are discarded, and “new” bits on the right are assigned
zeroes. Thus, shifting unsigned operand to the left by n positions is equivalent to multiplying it by
2n if all the discarded bits are zeros. This is also true for signed operands if all the discarded bits
are equal to sign bit. With shift right (shr), right most bits are discarded, and the “freed” bits on the
left are assigned zeroes (in case of unsigned operand) or the value of the sign bit (in case of
signed operand). Shifting operand to the right by n positions is equivalent to dividing it by 2n.
28

Expresions
An expression is a sequence of operators, operands and punctuators that returns a
value. The primary expressions include: literals, constants, variables and function calls.
From these, using operators, more complex expressions can be created. Formally,
expressions are defined recursively: subexpressions can be nested up to the limits of
memory. The way operands and subexpressions are grouped does not necessarily spec-
ify the actual order in which they are evaluated by mikroPascal.

Statements
Statements define algorithmic actions within a program. Each statement needs to be ter-
minated by a semicolon (;). In the absence of specific jump and selection statements,
statements are executed sequentially in the order of appearance in the source code.

Assignment Statements
Assignment statements have the form:

variable := expression;

The statement evaluates the expression and assigns its value to a variable. All rules of
the implicit conversion apply. Variable can be any declared variable or array element, and
expression can be any expression. Do not confuse the assignment with relational oper-
ator = which tests for equality.

Compound Statements (Blocks)


A compound statement, or block, is a list of statements enclosed by keywords begin and end:

begin
statements
end

Syntactically, a block is considered to be a single statement which allows using it when Pascal
syntax requires a single statement. Blocks can be nested up to the limits of memory. For exam-
ple, while loop expects one statement in its body, so we can pass it a compound statement:

while i < n do
begin
temp := a [i];
a [i] := b [i];
b [i] := temp;
n := n + 1;
end;

end.
In mikroPascal, the end. statement (the closing statement of every program) acts as an endless loop.
29

Conditional Statements
Conditional or selection statements select from alternative courses of action by testing
certain values.

If Statement
Use if to implement a conditional statement. Syntax of if statement has the form:

if expression then statement1 [else statement2]

When expression evaluates to true, statement1 is executed. If expression is false, state-


ment2 is executed. The expression must convert to a boolean type (true or false); other-
wise, the condition is ill-formed. The else keyword with an alternate statement (state-
ment2) is optional. There should never be a semicolon before the reserved word else.

Nested if statements
Nested if statements require additional attention. General rule is that the nested condi-
tionals are parsed starting from the innermost conditional, with each else bound to the
nearest available if on its left:

if expression1 then
if expression2 then statement1
else statement2

Compiler treats the construction in the following way:

if expression1 then
begin
if expression2 then statement1
else statement2
end

To force the compiler to interpret our example the other way around, we would have to
write it explicitly:

if expression1 then
begin
if expression2 then statement1
end
else statement2
30

Case Statement
Use the case statement to pass control to a specific program branch, based on a certain
condition. The case statement consists of a selector expression (a condition) and a list
of possible values. The syntax of case statement is:

case selector of
value_1 : statement_1
...
value_n : statement_n
[else default_statement]
end

The selector is an expression which should evaluate as integral value. The values can
be literals, constants, or expressions, and statements can be any statements.
The else clause is optional. When using the else branch, note that there should never be
a semicolon before the keyword else.
First, the selector expression (condition) is evaluated. The case statement then com-
pares it against all available values. If the match is found, the statement following the
match evaluates, and case statement terminates. In case there are multiple matches, the
first matching statement will be executed. If none of the values matches the selector, then
the default_statement in the else clause (if there is one) is executed.
Here’s a simple example of case statement:

case operator of
'*' : result := n1 * n2;
'/' : result := n1 / n2;
'+' : result := n1 + n2;
'-' : result := n1 - n2
else result := 0;
end;

Also, you can group values together for a match. Simply separate the items by commas:

case reg of
0: opmode := 0;
1,2,3,4: opmode := 1;
5,6,7: opmode := 2;
end;

Nested Case Statements


Note that case statements can be nested – values are then assigned to the innermost
enclosing case statement.
31

Iteration Statements
Iteration statements let you loop a set of statements. You can use the statements break
and continue to control the flow of a loop statement. The break terminates the statement
in which it occurs, while continue begins executing the next iteration of the sequence.

For Statement
The for statement implements an iterative loop and requires you to specify the number
of iterations. The syntax of for statement is:

for counter := initial_value to final_value do statement


// or
for counter := initial_value downto final_value do statement

The counter is a variable which increments (or decrements if you use downto) with each iter-
ation of the loop. Before the first iteration, counter is set to the initial_value and will increment
(or decrement) until it reaches the final_value. With each iteration, a statement will be exe-
cuted. The initial_value and final_value should be expressions compatible with the counter;
statement can be any statement that does not change the value of counter. Here is an exam-
ple of calculating scalar product of two vectors, a and b, of length n, using for statement:

s := 0;
for i := 0 to n-1 do s := s + a [i] * b [i];

Endless Loop
The for statement results in an endless loop if the final_value equals or exceeds the range
of counter’s type. For example, this will be an endless loop, as counter can never reach 300:
More legible way to create an endless loop in Pascal is to use the statement while TRUE do.

var counter : byte;


...
for counter := 0 to 300 do ...

While Statement
Use the while keyword to conditionally iterate a statement. Syntax of while statement is:

while expression do statement

The statement is executed repeatedly as long as the expression evaluates true. The test
takes place before the statement is executed. Thus, if expression evaluates false on the
first pass, the loop is not executed. Probably the easiest way to create an endless loop
is to use the statement:

while TRUE do ...;


32

Repeat Statement
The repeat statement executes until the condition becomes false. The syntax of repeat state-
ment is:

repeat statement until expression

The statement is executed repeatedly until the expression evaluates false. The expression
is evaluated after each iteration, so the loop will execute statement at least once. Here is an
example of calculating scalar product of two vectors, using the repeat statement:

s := 0; i := 0;
...
repeat
begin
s := s + a [i] * b [i];
i := i + 1;
end;
until i = n;

Jump Statements
A jump statement, when executed, transfers control unconditionally.

Break Statement
Sometimes, you might need to stop the loop from within its body. Use the break state-
ment within loops to pass control to the first statement following the innermost loop (for,
while or repeat block). For example:

Lcd_Out(1, 1, "No card inserted");


// Wait for CF card to be plugged; refresh every second
while TRUE do
begin
if Cf_Detect() = 1 then break;
Delay_ms(1000);
end;
// Now we can work with CF card ...
Lcd_Out(1, 1, "Card detected ");

Continue Statement
You can use the continue statement within loops to “skip the cycle”:

// continue jumps here // continue jumps here repeat


for i := ... do while condition do begin
begin begin ...
... ... continue;
continue; continue; ...
... ... // continue jumps here
end; end; until condition;
33

Exit Statement
The exit statement allows you to break out of a routine (function or procedure). It pass-
es the control to the first statement following the routine call.

Here is a simple example:

procedure Proc1();
var error: byte;
... // we're doing something here
if error = TRUE then exit;
... // some code, which won't be executed if error is true

Goto Statement
Use the goto statement to unconditionally jump to a local label — for more information,
refer to Labels. The syntax of goto statement is:

This will transfer control to the location of a local label specified by label_name. The goto

goto label_name;

line can come before or after the label. The label declaration, marked statement, and
goto statement must belong to the same block. Hence it is not possible to jump into or
out of a procedure or function.
You can use goto to break out from any level of nested control structures. Never jump
into a loop or other structured statement, since this can have unpredictable effects. The
use of goto statement is generally discouraged as practically every algorithm can be real-
ized without it, resulting in legible structured programs. One possible application of goto
statement is breaking out from deeply nested control structures:

for (...) do
begin
for (...) do
begin
...
if (disaster) then goto Error;
...
end;
end;
.
.
.
Error: // error handling code
34

asm Statement
mikroPascal allows embedding assembly in the source code by means of asm state-
ment. Note that you cannot use numerals as absolute addresses for register variables in
assembly instructions. You may use symbolic names instead (listing will display these
names as well as addresses).

You can group assembly instructions with the asm keyword:

asm
block of assembly instructions
end

Pascal comments are allowed in embedded assembly code. Also, you may use one-line
assembly comments starting with semicolon:

program test
var myvar : word;
begin
myvar := 0;
asm
MOVLW 10
MOVLW test_main_global_myvar_1
end;
end.

Directives

Compiler Directives
mikroPascal treats comments beginning with a “$” immediately following the opening
brace as a compiler directive; for example, {$ELSE}. You can use conditional compilation
to select particular sections of code to compile while excluding other sections. All com-
piler directives must be completed in the source file in which they begun.

Directives $DEFINE and $UNDEFINE


Use directive $DEFINE to define a conditional compiler constant (“flag”). You can use any
identifier for a flag, with no limitations. No conflicts with program identifiers are possible, as
flags have a separate name space. Only one flag can be set per directive. For example:

{$DEFINE Extended_format}

Use $UNDEFINE to undefine (“clear”) previously defined flag.


35

Directives $IFDEF..$ELSE
Conditional compilation is carried out by the $IFDEF directive. The $IFDEF tests whether
a flag is currently defined or not; that is, whether a previous $DEFINE directive has been
processed for that flag and is still in force. Directive $IFDEF is terminated by the $ENDIF
directive, and can have an optional $ELSE clause:

{$IFDEF flag}
<block of code>
{$ELSE}
<alternate block of code>
{$ENDIF}

First, $IFDEF checks if flag is defined by means of $DEFINE. If so, only <block of code>
will be compiled. Otherwise, <alternate block of code> will be compiled. The $ENDIF
ends the conditional sequence. The result of the preceding scenario is that only one sec-
tion of code (possibly empty) is passed on for further processing. The processed section
can contain further conditional clauses, nested to any depth; each $IFDEF must be
matched with a closing $ENDIF. Here is an example:

// Uncomment the appropriate flag for your application:


//{$DEFINE resolution10}
//{$DEFINE resolution12}

{$IFDEF resolution10}
// <code specific to 10-bit resolution>
{$ELSE}
{$IFDEF resolution12}
// <code specific to 12-bit resolution>
{$ELSE}
// <default code>
{$ENDIF}
{$ENDIF}

$I is compiler directive for inserting content of given file into place where this directive is
called. Here is an example:

{$I filename.txt}

Predefined Flags
mikroPascal has predefined flags which can be used to compile different sources for dif-
ferent platforms. For example, if target chip is P18F4520 you can use the P18F4520 flag
for conditional compiling:

{$IFDEF P18F4520}
// put some code for 18F4520
{$ELSE}
// put some code for other chips
{$ENDIF}
36

Linker Directives
mikroPascal uses internal algorithm to distribute objects within memory. If you need to
have a variable or a routine at specific predefined address, use the linker directives
absolute and org.

Directive absolute
Directive absolute specifies the starting address in RAM for variable. If variable is multi-
byte, higher bytes will be stored at the consecutive locations. Directive absolute is
appended to the declaration of a variable:

var x : byte; absolute $22;


// Variable x will occupy 1 byte at address $22

y : word; absolute $23;


// Variable y will occupy 2 bytes at addresses $23 and $24

Directive org
Directive org specifies the starting address of a routine in ROM. It is appended to the dec-
laration of routine. For example:

procedure proc(par : byte); org $200;


begin
// Procedure will start at address $200
...
end;

Constant agregates (struct, array) also can be put on specified address in ROM by
means of the org directive.

const ar : array [10] of char = {0,1,2,3,4,5,6,7,8,9}; org 0x400;


// const array will occupy 10 byte at address 0x400 in flash

Directive org can be applied to any routine except the interrupt procedure. Interrupt will
always be located at address $4 (or $8 for P18), Page0.

Directive orgall
This directive also specifies the starting address in ROM but it refers to all routines.

Directive volatile
Directive volatile gives variable possibilty to change without intervention from code.

var MyVar: byte; absolute $123; register; volatile;


37

Function Pointers
Function pointers are allowed in mikroPascal. The example shows how to define and use a
function pointer: Example demonstrates the usage of function pointers. It is shown how to
declare a procedural type, a pointer to function and finally how to call a function via pointer.

// First, define the procedural type


type TMyFunctionType = function (param1, param2: byte; param3: word) : word;

// This is a pointer to previously defined type


var MyPtr: ^TMyFunctionType;
Sample: word;

// Now, define few functions which will be pointed to. Make sure that
// parameters match the type definition.
function Func1(p1, p2: byte; p3: word): word;
begin
result := p1 and p2 or p3; // return something
end;

// Another function of the same kind. Make sure that parameters match
// the type definition

function Func2(abc: byte; def: byte; ghi: word): word;


begin
result := abc * def + ghi; // return something
end;

// Yet another function. Make sure that parameters match the type definition

function Func3(first, yellow: byte; monday: word): word;


begin
result := monday - yellow - first; // return something
end;

// main program:
begin
// MyPtr now points to Func1
MyPtr := @Func1;
// Perform function call via pointer, call Func1, the return value is 3
Sample := MyPtr^(1, 2, 3);
// MyPtr now points to Func2
MyPtr := @Func2;
// Perform function call via pointer, call Func2, the return value is 5
Sample := MyPtr^(1, 2, 3);
// MyPtr now points to Func3
MyPtr := @Func3;
// Perform function call via pointer, call Func3, the return value is 0
Sample := MyPtr^(1, 2, 3);
end.
38

A function can return a complex type. Follow the example bellow to learn how to declare
and use a function which returns a complex type.
This example shows how to declare a function which returns a complex type.

type TCircle = record // Record


CenterX, CenterY: word;
Radius: byte;
end;

var MyCircle: TCircle; // Global variable

// DefineCircle function returns a Record


function DefineCircle(x, y: word; r: byte): TCircle;

begin
result.CenterX := x;
result.CenterY := y;
result.Radius := r;
end;

begin
// Get a Record via function call
MyCircle := DefineCircle(100, 200, 30);

// Access a Record field via function call


MyCircle.CenterX := DefineCircle(100, 200, 30).CenterX + 20;
// |------------------------| |------|
// | |
// Function returns TCircle Access to one field of TCircle
No part of this manual may be reproduced, transmitted, transcribed, stored in a retrieval
system, or translated into any language in any form or by any means, without expressed
written permission of MikroElektronika company.

MikroElektronika provides this manual “as is” without warranty of any kind, either
expressed or implied, including, but not limiting to implied warranties or conditions of
merchantability or fitness for a particular purpose.

In no event shall MikroElektronika, its directors, officers, employees or distributors be


liable for any indirect, specific, incidental or consequential damages whatsoever (includ-
ing damages for loss of business profits and business information, business interruption
or any other pecuniary loss) arising from any defect or error in this manual, even if
MikroElektronika has been advised of the possibility of such damages.

Specification and information contained in this manual are furnished for internal use only,
and are subject to change at any time without notice, and should be construed as a com-
mitment by MikroElektronika.

MikroElektronika assumes no responsibility or liability for any errors or inaccuracies that


may appear in this manual.

Product and corporate names appearing in this manual may or may not be registered
trademarks or copyrights of their respective companies, and are only used for identifica-
tion or explanation and to the owners’ benefit, with no intent to infringe.

You might also like