picma
Contents
picma [options] [file...]
Options include:
- -h
- Print help text and exit
- -d types
- Generate debug output of various types:
l | Assembler listing
|
p | Passes
|
t | Code tree
|
a | Code allocation
|
f | Code freespace map
|
s | Struct freespace map
|
b | Stack backtrace
|
- -f type
- Select output format, type is either 'hex', 'ptf', or 'hp'.
- -m type
- Write a HTML memory map to stdout, of the given type. Type should be 'C', 'R', 'D', or 'P'.
- -o file
- Output code to the given file instead of stdout.
- -r
- On UNIX, swap '.' and '/' in included names. Ignored on RISC OS.
- -t
- Enable throwback (ignored on UNIX)
- -u
- Same as -r, except this is active on RISC OS and ignored on UNIX.
- -v
- Turns on an increasing number of -d options.
- -I path
- Defines or replaces the default search path when looking for
included files. On RISC OS, the default is 'picma:', on UNIX it is the contents of 'PICMA_PATH'.
Whitespace in the input is generally optional, and can be any mix of CR, LF, SPC, and TAB.
Anything between /* and */, and anything from // until the end
of the line is ignored. /*...*/ comments can also be nested.
Entities
- program
- A complete program consists of 1 or more files. The filenames can come from the command
line, where the files will be concatenated and treated as one big file. Other files can be
included with #include. The syntax of #include depends on the build environment:
RISC OS
- Directories are separated with '.', and the path is a ','-separated list of prefixes.
- #include "[path.]wildmask"
This searches in the directory holding the current file, or in
a subdirectory if the optional path is given.
All the files that match the given wildmask are included
as they are found.
- #include <[path.]wildmask>
This searches the include path (see -I above), adding the
optional path given, and finds the first directory that
contains one or more files matching the wildmask. All
the matching files in this directory are then included.
UNIX
- Directories are separated with '/', and the path is a ':'-separated list of directories.
- #include "[path/]wildmask"
This searches in the directory holding the current file, or in
a subdirectory if the optional path is given.
All the files that match the given wildmask are included
as they are found.
- #include <[path/]wildmask>
This searches the include path (see -I above), adding the
optional path given, and finds the first directory that
contains one or more files matching the wildmask. All
the matching files in this directory are then included.
Windows
- This is apparently a relatively new OS used by an unknown number of people. No build is
planned for that OS until it matures.
In all cases the files are inserted in place of the include command.
Another relevant command is #include_only_once. If this is encountered in a file that
has already been included, the parsing stops as if the file ended at that point.
All this produces one long sequence of statements.
These statements are implicitly inside an unseen block, which is in turn attached to a
global code statement. This is important because that global code statement
has the base = P0 option set, which means that any instructions present in the top
level will be stored from P0.
- block
- A block is 0 or more statements enclosed in { }. Every block has its own local
name scope, and variables are looked up by working outwards from the current block.
Each block also has local begin and end labels, pointing to the first word and
the one after the last, respectively. These are constants, and cannot be changed.
- statement
- There are several types of statements:
- expr ;
- This simply evaluates the given expr. Since the result is thrown away, this is
most useful for expressions that end up changing variables.
There is a special case of this statement:
varref ( [ expr ] [ , [ expr ] ... ] ] ) ;
This would normally call the named function and throw the result away. Instead it invokes
the named macro with the given argument list. If there are more args than the macro takes,
the rest are ignored. If there are less, the missing macro parametres will
exist, but be unassigned.
- #prefix name1 = name2 ;
- This defines a prefix in the current block called name1 with the value
name2. See below for more information on prefixes.
- block
- This creates a block containing the enclosed statements.
- instr [ expr [ , expr ... ] ] ;
- This stores the given instruction at the current word.
A valid instruction is one from the following list, or their uppercase versions:
addlw, addwf, andlw, andwf, bcf, bsf, btfsc, btfss, call, clrf, clrw,
clrwdt, comf, decf, decfsz, goto, incf, incfsz, iorlw, iorwf, movf, movlw, movwf, nop,
retfie, retlw, return, rlf, rrf, sleep, sublw, subwf, swapf, xorlw, xorwf.
Or any of the aliases:
addwl, andwl, iorwl, movwl, retwl, subwl, xorwl.
- name:
- This defines a label (an area in the program memory) called name.
- area expr , [ newvar [ := expr ] ]
[ , [ newvar [ := expr ] ] ... ] ;
- This defines a number of vars, based on the area given. If any of the the newvars
are given values, the relevant areas are initialised too.
The first var defined will have the area value given. The next will have the same width,
but placed just after the previous var, and so on. You can also omit both the newvar
and the value, which will just skip the area that you've come to.
- alloc varref , { members } ;
- This creates a local copy of the named area, if it doesn't exist already, and allocates a
number of subset areas within it. The members have the same syntax and meaning as
structure members. In this case they are created as constants, in
the scope of the alloc statement.
When the names go out of scope, so does the local copy of varref, and the areas
allocated are in effect freed for reuse by subsequent alloc statements.
Once you have alloc'ed from an area, it (locally) shrinks by the amount
used, and any other references to that area within that scope will use the new, smaller,
area. If this is done in the top level (or varref starts with '?'), it will already
exist, so there will be no local copy, it will never go out of scope, and you never get
the allocated space back.
- chip string ;
- This statement predefines all the chip\* variables to standard values for the
given chip. There can be more than one, but they must all use the same chip name.
The following chip names are recognised:
12F629, 12F675, 16F84, 16F627, 16F628, 16F872, 16F873, 16F874, 16F876, 16F877.
- code varref options block
- This defines a code block with the options given.
It creates a constant word area as varref which points to the start of the code
block, and has the same width.
Note that creating a code block (rather than just a block), also declares that the
code is relocatable, and it may be placed anywhere (unless there is a link
statement for it).
The possible code options are listed below. The options are given as name/value pairs
enclosed in ( ). The list can be empty, and the brackets may then be omitted.
- base = expr
- Force the start of the code block to be at the address specified (which must be
a program area).
- nowrap [ = expr ]
- Prevent the code block from crossing a block boundary, with the block size given
(or 256 if none is specified). If this option is not present, the chip still has a
default block size that will apply to the code block.
- optional
- This declares the code block optional. If it ends up unused, it will not be included
in the output. There are certain rules that must be followed to use this feature
safely:
- A block is considered "in use" if its name-label is used in a call,
goto, link, or uses statement. This includes inside
the block itself, so you should use "begin" instead, if you want to refer to the start.
- Any other jump to the block, e.g. using a calculated address, will not be spotted.
Hence, if you have optional code blocks that might be called in this way, you
should add appropriate uses statements to make sure they are not
removed.
- When a block is removed, it generates a warning. Use option\warning\cr
to turn it off.
- Don't make the reset code optional (or any other code on a hardware vector).
Chances are it is not called (by you), and hence will be removed. Bad thing.
- Note that the statements inside a removed block will still be executed, it just won't
generate any code or labels. If these have global effects, they will still happen.
You can have several code blocks of the same name in the same scope. There will only be one
label, and it points to the last block defined. This requires that all the other blocks are
optional, otherwise it results in an error about reassigning that label. The last
block (which is the only one not removed) may also be optional, but that is not required.
For each code block, a local variable called "!name" is created, containing the fully expanded
name of the code block (i.e. without prefixes). This is an internal variable, and it can only
be accessed via the @ operator, as in @"!name".
- def newvar [ = expr ] [ , newvar
[ = expr ] ... ] ;
- This defines one or more variables, with optional initial values. When creating constants,
giving intitial values is mandatory.
- do block while expr ;
- This will execute the block, and keep doing that until the expr is 0.
- enum [ varref [ = expr ] ] [ , [ varref
[ = expr ] ] ... ] ;
- This is another way of creating areas, where each of the varrefs can define its own
area. The first area will have the value 0 if none is specified. The next will increase
like in the area object, as long as no specific value is given. If both name
and value is omitted, the value is increased like normal, but not assigned to anything.
All variables created will be constants.
- error expr [ , expr ... ] ;
- This makes the assembler output the given exprs as an error, and stop.
- every expr , ( [ expr ] [ , [ expr ] ...
] ] ) ;
- This expands all macros that match the given wildcarded expr (which must produce a
string), passing the arguments to each of them. The matches are expanded in alphabetical order.
If no macros match the expr, nothing happens (it is not an error).
- for ( expr ; expr ; expr ) block
- This will evaluate the first expr, and while the second expr is non-0, it
will execute the given block and then evaluate the third expr after each
execution.
- foreach ( varref = expr ) block
- This will expand the given block once for each variable that matches the wildcarded
string expression. The matches are processed in alphabetical order. For each expansion,
varref is set to the name of the matched variable. The value can be extracted using
@varref.
Make sure you only match variables that are defined at the point of the foreach
statement. If more matching names are created later, the next pass may create more code,
and you will get an error about that.
- func name ( [ varref [ , varref ] ... ]
) block
- This defines a function of the given name, taking the listed parameters.
The code in block is executed when the function is evaluated. There are certain
statements you can't use in the block, namely all the ones that can generate data to go
in the output. This includes all PIC instructions, label definitions, macro calls,
area, init, and alloc.
There is an exception to this
rule: Inside functions return expr ; is allowed, but instead
of generating a return instruction, it makes the function return with the given expr
as result.
- if expr block
if expr block else block
- This evaluates the expr as a scalar, and if non-0, executes the first block
given. Otherwise the second block is executed if it is present.
- init expr := expr [ , expr :=
expr ... ] ;
- This initialises the given areas.
- link expr ;
- expr is evaluated, and the result must be a string. This will ensure that the named
codeblock is assembled at the current position. There are rules:
- The name must match an existing code block label (i.e. no forward references).
- There can be 0 or 1 link statements for each code block.
- Linked blocks are always optional, and will be removed if the enclosing block is
removed.
- The base and nowrap options can also be used for the linked block.
The enclosing block will not be moved because of these, they will just generate errors
if they end up wrong.
- macro name ( [ varref [ , varref ] ... ]
) block
- This defines a macro of the given name, taking the listed parameters.
The code in block is expanded when the macro is invoked. The macro name can be used
according to the same rules that apply to variables, except you can use forward references
to macros.
- print expr [ , expr ... ] ;
- This will print (to stdout) all the exprs given, evaluating each one. It finishes by
printing a newline.
- return expr ;
- This is valid inside functions only. The function returns with the given value.
- switch expr { cases }
- The expr is evaluated, and compared with the cases until a match is
found. That block is then executed.
There are two types of cases:
- case expr block
- The block is executed if the expr matches the one in the enclosing
switch.
- default block
- The block is executed if none of the other cases matched.
There can be 0 or more case blocks, and 0 or 1 default block.
- uses expr ;
- This statement takes a string, which must be a code block name. The named
code block is then considered "in use", and won't be removed if it is optional.
- warning expr [ , expr ... ] ;
- This makes the assembler output the given exprs as a warning.
- while expr block
- This will evaluate the expr, and while it is non-0, execute the block.
- newvar
- This is a variable that will be created. It is an varref, optionally prefixed by
const (which creates a constant variable).
- varref
- This is an entity that can be written to, i.e. a variable reference. It can be either
a name or @( expr ). The latter will evaluate the expression,
and use the result as the name (which must become a string).
Writing @(" name ") is the same as writing name.
- name
- An identifier used for variables, macro names, etc. It is a string of 1 char from the set [A-Za-z_¬\],
followed by 0 or more from [0-9A-Za-z_¬\]. names are case sensitive.
There are certain reserved words and constructs that cannot be used for a name.
Here is a list of reserved names:
- Preset variables, e.g. true, false, chip\name, etc.
- Keywords, e.g. area, enum, init, etc.
- Instructions, e.g. addlw, addwf, andlw, etc. (and their uppercase versions)
- Aliases, e.g. addwl, andwl, etc.
- Literal areas
The easiest way to avoid reserved names is to use mixed case, e.g. 'Output'. If short enumerated
names are desired, use lowercase like d0, d1, d2, etc. to avoid making an area instead.
Global names
By prepending '?' to a name, you can refer to the global version of that variable. This works
everywhere a name is acceptable, but it doesn't make sense in all cases. You can for example
create a global var in a loop, but if it runs more than once, you get an error trying to create it
the second time. The solution is of course to create it outside the loop, and only assign it inside.
A block can have any number of prefixes defined, using the #prefix statement. You can then
use names like foo#bar, where 'foo' is the name of a prefix. The value of this prefix will
then be used instead of 'foo#' when looking up the variable. This works for variables, macro names,
function names, and code block names. The prefix name must be a plain name, and can't contain
'?' or '#'. The value can contain a prefix itself, but not '?'.
When searching for the named prefix, the source structure is used (rather than the
execution structure used for everything else). This means that the parent of a macro definition is the
block where it is defined, and not the block where it is invoked.
Example:
#prefix foo = baz;
def foo#bar = 5; // this creates a variable called "bazbar"
Note: When using '?' in combination with prefixes, the prefix has higher precedence. This means
that ?foo#bar will search for the 'foo' prefix in the current block (and then the parent,
etc.), but the resulting name is then looked up globally.
- expr
- An expression, containing 1 or more names, literals, or exprs in
combination with operators. Valid expressions, highest precedence first:
Precedence
| expr
| Effect/Result
|
---|
17
| ( expr )
| expr
|
16
| expr -> expr
| Structure operator, see structures
|
15
| expr [ expr ]
| Indexed subset of area, indexed character from string, or indexed sub-structure. See also
structures.
|
15
| name++
| Return name, then increase it
|
15
| name--
| Return name, then decrease it
|
15
| varref ( [ expr ] [ , [ expr ] ... ] ] )
| Return value after calling the named function with the given arguments
|
15
| bytes( expr )
| Size of an area, a string, or a structure, rounded up to a whole number of bytes. For a scalar
it is (scalar + 7) / 8.
|
15
| bits( expr )
| Exact size of an area, a string, or a structure, in bits. Returns a scalar unchanged.
|
15
| asc( expr )
| ASCII value of the first character of the string expr. Returns 0 if given a null string.
|
15
| chr( expr )
| A string containing the character that has an ASCII value of expr. Returns a null string
if expr is 0.
|
15
| now()
| The current date and time as a string.
|
15
| def( expr )
| True if @(expr) exists
|
15
| type( expr )
| Returns the type of @(expr) as a string:
"scalar" | for scalars
| "string" | for strings
| "area C" | for configuration areas
| "area R" | for register areas
| "area D" | for data areas
| "area P" | for program areas
| "struct" | for structures
| "" | for existing, but unassigned vars
|
|
15
| left( expr1, expr2
)
| The first expr2 chars of the string expr1
|
15
| right( expr1, expr2
)
| The last expr2 chars of the string expr1
|
15
| mid( expr1, expr2
)
| The last part of the string expr1, starting at position
expr2 (0 is the first position)
|
15
| mid( expr1, expr2,
expr3 )
| expr3 chars of the string expr1, starting at position
expr2
|
15
| str( expr )
| expr as a string (same as print would output)
|
15
| firstleft( expr1, expr2
)
| Takes two string expressions. Returns the left part of expr1, up to but
excluding the first occurence of expr2 (searching from left).
|
15
| firstright( expr1, expr2
)
| Takes two string expressions. Returns the right part of expr1, down to but
excluding the first occurence of expr2 (searching from right).
|
15
| lastleft( expr1, expr2
)
| Takes two string expressions. Returns the left part of expr1, up to but
excluding the last occurence of expr2 (searching from left).
|
15
| lastright( expr1, expr2
)
| Takes two string expressions. Returns the right part of expr1, down to but
excluding the last occurence of expr2 (searching from right).
|
14
| expr . expr
| Convert area to bit area, with given bit offset
|
14
| expr ` expr
| Change width of area
|
13
| ++name
| Increase name, then return it
|
13
| --name
| Decrease name, then return it
|
13
| ../expr
| expr evaluated in the parent scope
|
13
| @expr
| Reference to a variable, whose name is given in the string expression.
|
13
| $expr
| Scalar value. The base offset of areas and structures, and the numerical value of strings.
|
13
| &expr
| Bit offset of area or structure
|
13
| *expr
| Mask of area or structure
|
13
| ~expr
| Binary NOT
|
13
| !expr
| Logical NOT
|
13
| -expr
| Negation
|
12
| expr * expr
| Multiplication
|
12
| expr / expr
| Division
|
12
| expr % expr
| Modulus
|
11
| expr + expr
| Addition, and string concatenation
|
11
| expr - expr
| Subtraction
|
10
| expr >> expr
| Binary shift right
|
10
| expr << expr
| Binary shift left
|
9
| expr >= expr
| Logical greater than or equal
|
9
| expr <= expr
| Logical less than or equal
|
9
| expr > expr
| Logical greater than
|
9
| expr < expr
| Logical less than
|
8
| expr != expr
| Logical difference
|
8
| expr == expr
| Logical equality
|
7
| expr & expr
| Binary AND
|
6
| expr ^ expr
| Binary EOR
|
5
| expr | expr
| Binary OR
|
4
| expr && expr
| Logical AND
|
3
| expr ^^ expr
| Logical EOR
|
2
| expr || expr
| Logical OR
|
1
| name = expr
| Assigns value of expr to name, and returns that value
|
1
| name += expr
| Adds (or concatenates) expr with name, and stores and returns the result
|
1
| name -= expr
| Subtracts expr from name, and stores and returns the result
|
1
| name *= expr
| Multiplies name by expr, and stores and returns the result
|
1
| name /= expr
| Divides name by expr, and stores and returns the result
|
1
| name %= expr
| Calculates name modulus expr, and stores and returns the result
|
1
| name |= expr
| Ors expr with name, and stores and returns the result
|
1
| name &= expr
| Ands expr with name, and stores and returns the result
|
1
| name ^= expr
| Eors expr with name, and stores and returns the result
|
1
| name <<= expr
| Shifts name left by expr bits, and stores and returns the result
|
1
| name >>= expr
| Shifts name right by expr bits, and stores and returns the result
|
A literal can have one of 4 different types:
A scalar is a one-dimensional integer. Literal scalars can be written in various ways:
- Prefixed with 0b, it is in binary.
- Prefixed with 0x, it is in hexadecimal.
- Prefixed with 0, it is in octal.
- Any other number is in decimal.
- A character constant enclosed in single quotes. This is either a single character,
or an escape sequence from the list below:
Char | Written as | Char | Written as
|
---|
BEL | \a | FF | \f
|
CR | \r | BS | \b
|
VT | \v | TAB | \t
|
LF | \n | \ | \\
|
? | \? | ' | \'
|
" | \" | |
|
It is also possible to use \ooo and \xhh to denote the
corresponding char in octal or hexadecimal.
An area is a 2-dimensional extent. One dimension is the type of memory,
which can be configuration memory (C), registers (R), EEPROM data (D), or
program memory (P). The second dimension is the offset within this
memory. Finally, it also has a size. Literal areas are written like
this:
areatype scalar
where areatype is either 'C', 'R', 'D', or 'P', defining the type of memory, and the
scalar is the offset in words. The size of a literal area is always 1 word.
Example: D10: 1 word at offset 10 in the EEPROM data memory.
The . and ` operators can be used to change the properties of an area.
area . expr changes the area from word to bit, giving it a bit-offset
equal to the scalar value of expr. The size of the area changes to 1 bit.
area ` expr changes the size of the area to the scalar value of expr.
The size is measured in either bits or words depending on the type of area given.
Examples:
D10.0: 1 bit at (word-)offset 10 in the EEPROM data memory.
D10.0`4: 4 bits at (word-)offset 10 in the EEPROM data memory.
D10`4: 4 bytes at (word-)offset 10 in the EEPROM data memory.
A string is a sequence of max. 511 chars enclosed in double quotes. Character constants can also
be used within strings (with the same behaviour as in scalars). These are C
strings, so they can not contain \0 (NUL).
Example: "foo\n": The word 'foo' followed by an LF.
A structure is a collection of data areas called members. Structures can be in two states,
prototypes and instances. The prototypes have everything except a base address, and can be used in
calculations where only the size is used. When a base address is attached (using ->),
the value becomes an instance of the prototype, and can be used where actual addresses are needed.
All structures are treated as normal values, and can be stored in variables, used within expressions,
etc.
A literal structure prototype is written like this:
struct { member [, member ]... }
Each member has a unique name within a structure, and can take 3 forms:
- name = bit [ .expr ] [`expr ]
A bit area with an optional fixed offset (in bits) and the given width in bits (default 1).
- name = byte [ .expr ] [`expr ]
A byte area with an optional fixed offset (in bytes) and the given width in bytes (default 1).
- name = expr [ .expr ] [`expr ]
This bases the member on the given expr, which must evaluate to a structure. This can
optionally be placed on a fixed offset (in bits). Giving a width of n means that the
referenced structure will be repeated n times.
Note that the syntax allows name to start with '?', and/or be a @(...)
reference. This is because the same syntax is used in alloc. If you use anything except
a plain name in a structure, it will be allowed (the space will be allocated), but you will not be
able to reference the member with ->.
Example:
def x = struct {
foo = bit`6, // 6 bit wide bitfield
bar = byte, // one single byte
baz = bit.14`2, // two bits at offset 14 (byte 1, bit 6 and 7)
quux = struct { // inline sub-structure
flop = bit.0 // one bit fixed at offset 0
}.20`3 // makes 3 copies of the sub-structure (total of 3
// bits) and places it at offset 20
};
The structure operator -> has 3 different modes of operation, depending on the
type of the first argument given:
- area -> struct
This creates an instance of the given structure, with the given area as base address. The
area must be greater than or equal to the size of the structure. A warning is generated if the given
struct is an instance instead of a prototype (but it is still allowed to happen). If the
struct requires byte-alignment, it is an error to base it on a bit area with a bit offset
other than 0. See below for details on alignment rules.
- struct prototype -> member
This returns the size of the given member in bits. You can use bytes() on this to get
the whole number of bytes.
- struct instance -> member
This returns a value representing the given member. If the member is a plain bit or byte field, a
normal area is returned. Otherwise an instance of the given sub-structure is returned, which can
then be selected from with a further -> if desired.
The [ operator has special behaviour for structures. Normally it uses the "natural size"
of the base area to index a given item. When used on a structure, it uses the size of the structure
as the "natural size". This means that in the example above, the 3 copies of the sub-structure
can be referenced as x->quux[0], x->quux[1], and x->quux[2].
Note that there is no difference between x->quux[0] and x->quux.
This is because there are no arrays in this language (it just looks like it). So there are not really
3 structures there, just one that takes 3 times the space.
There are special rules for alignment of structures and their members. These rules only apply if there
are byte members involved, which must be byte-aligned to be useful. If you want byte-size
fields without the alignment, use bit members of size 8.
Any byte members in the structure will get byte-aligned offsets. This alignment is contagious, and
the structure itself will also have to be byte-aligned to ensure that the members are. If this structure
is then used as part of another structure, it infects that as well, and it will also require byte
alignment. In certain cases the byte-alignment also spreads to the size of the structure, for example if
there are more than one in a sequence. Padding bits may be inserted between them so each copy is
byte-aligned. These bits are 'lost', i.e. it is currently impossible for the member allocation code to
place small bitfield members within these padding spaces. This may be added in the future, so don't
assume that the padding bits are free for other uses.
There are a few predefined global constants: w=0, f=1, false=0,
true=1.
Options
Also, there are many preset options that can be changed. All of these have the form
option\type[\name].
- Type "warning"
- All of these can be turned on or off. The value 0 switches the warning off. Any other scalar
switches the warning on. They all default to 1 unless changed.
variable | Warning
|
---|
option\warning\op | Overwriting program memory at offset ...
|
option\warning\ss | Using string value as scalar
|
option\warning\as | Using area base as scalar
|
option\warning\bw | Using bit area as word area
|
option\warning\pw | Using partial area as whole word
|
option\warning\mb | Using multi-bit area as one bit
|
option\warning\rb | Replacing bit offset
|
option\warning\wb | Wrapping bit offset
|
option\warning\wa | Wrapping area offset
|
option\warning\wd | Wrapping destination specifier
|
option\warning\wn | Wrapping bit number
|
option\warning\ge | Jump to expression, ...
|
option\warning\cr | Code '...' not in use, removed
|
option\warning\movf | Default destination is f, ...
|
option\warning\bitwrap | Bitfield x`y crosses byte boundary, ...
|
option\warning\rebase | Rebasing struct
|
option\warning\fw | Having a local 'f/w' is...
|
option\warning\oldalloc | This code uses the old alloc syntax
|
Example: option\warning\bw = false;
- Type "print"
- There is only one of these. It turns off the print statement if set to false.
It actually turns it off, i.e. the arguments will not be evaluated, so any side effects
from these will stop happening.
Example: option\print = false;
- Type "list"
- These control the assembler listing (enabled by -v). They are all on by default.
variable | Effect
|
---|
option\list | If set to false, it disables the assembler listing.
|
option\list\file | Enables printing of the filename.
|
option\list\line | Enables printing of the line number.
|
Example: option\list = false;
- Type "ptf"
- These allow you to set the headers when the output format is ptf. Normally they will not
exist, so the headers are omitted. When created (using def) the relevant header
lines will be included. They must be created in the global scope to have any effect.
variable | Effect
|
---|
option\ptf\name | Creates a header line called name.
|
Example: def ?option\ptf\Date = now();
Note that there is a big difference between
option\foo = ... and
def option\foo = ...
In the former case, you are changing the existing value, which would usually be the global
setting. In the latter case, you create a new local option, and the selected value only applies
within the current block. This is exactly like all other variables.
Chip characteristics
Another set of variables define the limits of the target chip. The complete set is:
variable | Contents
|
---|
chip\name
| This is just a string giving the name of the chip. It is mainly used to ensure that
you don't accidentally include libraries written for different incompatible chips.
|
chip\size\config
| This is the size of the configuration area, in words.
|
chip\size\data
| This is the size of the EEPROM data area, in bytes.
|
chip\size\program
| This is the size of the program area, in words.
|
All of these are set to default values if you use the chip command. This will also
create them as constants. Otherwise they behave exactly like normal variables.
If you need to target a chip that is not in the list, you can set the limits yourself.
For example:
def chip\name = "24F951";
def chip\size\config = 16;
def chip\size\data = 8192;
def chip\size\program = 4194304;
It is not strictly necessary to set a name, it is only used for headings, etc. But it does make
it a lot easier when you come back to the code later.
The various variables need to be defined before they are needed. The compiler will only check
this immediately before it needs the value, so if you get strange errors about chip\size\*,
it is probably because it hasn't been defined in time.
A variable can be in one of 3 major states. In macro and function code, it can be necessary to
determine the state of an argument. def() and type() is used for this:
State | def() | type()
|
---|
Does not exist | false | Gives an error
|
Exists, unassigned | true | ""
|
Exists, assigned | true | String giving type
|
When calling a macro or function with some arguments missing, the affected
parameter variables exist, but are unassigned.
Some operators work on more than scalars, and behave in various weird ways when applied to areas.
This is described below, see also structures for other strange effects.
- $area
- Returns the scalar value of the area or structure, which is the word offset.
- *area
- The mask of the area, which is a scalar containing a bit mask with 1's covering the area.
This only works for bit areas.
- &area
- Bit offset of the area. This only works for bit areas.
- area+scalar
scalar+area
- Returns another area, covering the same width as the original, but offset from it by
width*scalar bits. For example,
- D0.1`2 + 1 = D0.3`2
- D0.1`2 + 2 = D0.5`2
- D0.1`2 + 3 = D0.7`2 (not very useful, since you can't currently have
bit areas covering more than 1 word).
For word areas, this always works, and offsets by width*scalar words instead.
- area-area
- Works for areas of the same type, and the same kind (word or bit). In each case, a new area
is returned, covering the space between the base offsets of the two areas. The widths are
ignored, and the result is always the absolute value. For example:
- D0.1 - D0.6 = D0.1`5
- D4 - D5 = D4`1
If the result is a bit area covering more than one word, an error is returned.
- area < area
area > area
area <= area
area >= area
area != area
area == area
- Allowed for areas of the same type. The result is a comparison of the base offsets if they
are different, otherwise the widths. Word and bit areas can be compared, and will return
results depending on the actual width. E.g. a word area of width 1 will be equal to a bit
area covering the whole word (8 or 14 bits depending on type). This is necessary to return
consistent results, so (a >= b) returns true if (a == b) and (a < b) is always the
opposite of (a >= b) etc. etc.
- area[scalar]
- This is also an operator, it returns an indexed subset of the area, measured in the "natural
size" (1 word or 1 bit).
Initialising an area (with area or init) will register the values as the
initial content of that area. This can be done with the config and data areas, and will cause
the data to be included in the output file at the appropriate addresses.
You can initialise an area to a string. With a word area, the characters
are simply stored at increasing word offsets.
If it is a bit area, every character will be stored at increasing area locations (increasing as
if adding 1 to the area after every char).
Example: area D3.0`4, foo = "12345";
This will store 0x21 at offset 3, 0x43 at offset 4, and 0x5 in the low nibble of offset 5. This
is because '1' (0x31) truncated to the width of the area (4 bits) is 0x01, so D3.0`4 becomes 0x01.
The next area is D3.0`4 + 1 = D3.4`4, which becomes 0x02, giving 0x21 in the whole byte. And so on.
Here are a few things that are nice to know when you're writing in this language.
Tip #1: This is not C
The C-like syntax can easily make you slip into "C-mode" mentally.
This should be avoided, since it is really quite different. For example, a perfectly normal
thing like this:
n = 0;
while (n < 5) {
print n++;
}
...will completely fail to do what you'd expect and instead become an infinite loop.
The reason for this is important to know, since it affects a lot of constructs in this
language.
Since this is an assembler, it makes several passes over the code to deal with forward
references. Some statements are run on every pass (e.g. while), but others, like
print and PIC instructions, are only executed on the last pass. This is necessary
so they can use references to variables that don't yet exist, but will be created in a later
pass.
But it also means that side effects should be avoided in these commands, since they will
only happen at the last pass. That is why the above becomes an infinite loop - the
n++ is not evaluated in the first pass, so the loop never ends.
The statements that execute on every pass are:
alloc, area (except init data), code, def, enum, expr;, every,
for, foreach, if, init (except init data), link, switch, while, & return
(from function).
All of these are safe to use side effects in. For the same reason, they can't use variables
that are forward-referenced.
The rest are usually only executed once, on the last pass. Of course, it's slightly more
complicated than this - in certain circumstances they are executed on every pass too.
If the interpreter is currently evaluating a function call (in any code pass), commands like
print, error, & warning will be executed, macro invocations and every
will be ignored, while PIC instructions will generate errors.
This means that the above code could be changed to:
n = 0;
while (n < 5) {
-echo(n++);
}
func echo(x)
{
print x;
return 0;
}
...and it stops being an infinite loop.
There are several things to note here:
- print is now run on every pass, so the list of numbers comes out about 10 times.
- The - in front of the function call is a way to avoid it being interpreted as a
macro invocation.
- Functions must return a value.
The above is still useless, so here's the "correct" way of writing it:
n = 0;
while (n < 5) {
print n;
n++;
}
Now n++ has become an expr; statement, so it is evaluated every
time, and the loop terminates properly. print is only executed on the last pass, so
you get only one list of numbers out.