A lot has been done over last few years to perfect the command line compiler,
but there are still few quirks there, and some system libs certainly need modifications. Problems already fixed in versions before 6.40 are not presented here. Note that version 6.50 has not brought changes to compiler, version 6.61 solved only one problem (introduced in v 6.40), and version 6.6.2 introduced new error. Fortunately, first time in last two years, version 7.00 significantly reduced the number of errors. Unfortunately, version 7.2 introduced unnerving IDE quirks.
common quirks and errors (present in earlier and current compiler version)
- quirks in fixed and floating-point math libraries are enumerated in respective parts of replacement libraries description.
Additionally, in case of PIC16 and enhanced family, 8-bit signed multiplication routine produces results depending on order of operands:
-1 * -128 = 0
-128 * -1 = -128
1 * -128 = 0
-128 * 1 = -128
- results of runtime calculations may differ from compile-time ones
The reason is two-fold: different evaluation rules are applied in both
cases and there are some imperfections in math libraries.
Differences for integral types due to evaluation rules were removed in v7.00, but those for floating-point arithmetic are still present, like in
y:=2E-38/x*10E5;
Here, for x=2, result will be zero at runtime but a nonzero (accurate) value at
compilation time. Again, the difference is due to partial result being outside of range.
Compile calculations do not fully mimic the Microchip floating-point range.
Some special cases are evaluated differently due to math libraries peculiarities, like
modulo operations with negative integers:
Byte2 := Byte1 mod -1;
If Byte1 is negative, result will be negative at compile-time, but positive
at runtime.
Other special cases giving wrong results at runtime:
- division of short by 0 gives -128, whatever the dividend's sign
- division of short -128 by -1 gives -128
- division of positive odd and negative even short numbers by -128 gives 1, not 0 (fixed in v5.00)
- division of integer or longint number by 0 gives positive value,
whatever the dividend's sign
- error in 32-bit signed division routine leads to remainders smaller by one in
magnitude if dividend equals minimum longint value which leads to incorrect results
of modulo operations
BTW, the example of a floating-point expression presented above will, for x=0, result in a
small value instead of infinity (represented by maximum positive value allowed in Microchip
format). It may also be a problem in some cases that division by zero of negative
argument gives positive value.
- mixing bit and boolean logic in one expression may lead to errors
There are too many cases when it may go wrong, so the general conclusion is - don't mix these two types of logic.
In fact, even using boolean logic with bits may lead to errors, like in
if not((bit1=1) and bool_var) then // always evaluates true if bit1=1
because compiler tries to use both bit logic and boolean logic at the same time (in spite of bit1=1 being boolean expression).
Even pure bit logic not always works, like in
if not PORTB.i then // always evaluates true
(i is a variable here).
If one needs to mix bit and boolean logic then my System replacement library contains function bool that may be applied to bit part of expression fixing the problem, like
if not(bool(bit1) and bool_var) then
if not bool(PORTB.i) then
- while evaluating in main a byte holding sbits declared in a unit
optimizer may overlook changes applied to single bits
An example will surely explain this one better than mere words. Lets assume that in a certain
unit a holding byte, bvar, is declared together with some of its bits defined as sbits
var
bvar: byte;
bvar1: sbit at bvar.1;
bvar4: sbit at bvar.4;
Now, if one wants to use these in main, for example by clearing the holding byte and then
manipulating the sbits
bvar:=0;
bvar1:=1;
bvar4:=1;
PORTB:=bvar;
one will be unpleasantly surprised by PORTB being cleared. The problem is in optimizer
seeing no connection between the holding byte and sbits. Simplest way to avoid it is to
declare the holding byte volatile thus preventing optimizer from assuming any previous
state of this variable.
- applying sfr modifier to a variable declared at absolute address in GPR space makes
the linker believe it's indeed in SFR space, so it's real space is freed
and may be taken by another variable
- FSR registers are not saved in ISR unless used in assembly
Not much explaining is required here - if one used indirect addressing in Pascal code (like
in next example) both in main code and ISR(s), one may have to save & restore used FSR
registers in ISR(s).
- modifying variables with the use of FSR registers may confuse optimizer
Again, an example should make the problem demonstrated clearly:
w2:=0;
//asm nop end;
FSR0Ptr:=@w2;
INDF0:=5;
w2:=w2+1; // w2=1 not 6
Without the commented line, optimizer will overlook changes done to w2 through indirect
addressing and assume old value still valid. The short assembly insert blocks optimizer
leading to proper evaluation of w2. Same may be achieved by declaring w2 volatile.
- including units in Project Manager leads to complete disregard of any
unit dependencies introduced with uses clauses (any unit included in PM
is connected directly to main - like there was a uses clause)
- placing local vars in rx space is ineffective as they're not reused;
when used in libs, may cause linker error for lack of space;
moreover, rx-ed local vars have global scope!
- FSR registers are used extensively by compiler
For example, single statement in following procedure uses all three FSRs of a PIC18
processor
procedure test(var bb,bc:byte);
begin
bb:=bc+bb;
End;{test}
- empty 'badram' declaration in *.mcl file causes compiler to assume that 0 is bad RAM location.
Happily enough it does not happen frequently - few processors, like PIC18F87K22, have no "bad RAM" locations. Still, compilers behaviour causes unnecessary problems with more advanced coding and one has to modify the mcl file declaring some dummy 'badram' location.
- System and Math library for enhanced processors series contain errors that may lead to overwriting of unrelated parts of RAM.
This may happen when handling large (>255 bytes) memory chunks or while performing math division when divisor happens to be zero.
- unlike for P16 and P18 processors, for enhanced family processors a call to assembly routine, even within single assembly block, causes compiler error
('Invalid ASM instruction').
- with P18FxxK40 processors, use of any of EEPROM library routines requires setting the NVMREG1 bit
of NVMCON1 register afterwards - otherwise some libraries (ones requiring accessing flash directly) may no longer function (fixed in v7.20).
- some libraries (like GLCD) do not function properly with P18FxxK40 processors due to different distribution of TRISx and PORTx registers in these processors
(fixed in v7.20)
(libraries controlling port pins are based on assumption of identical relative spacing of these registers in all PIC18 processors).
- division by constant being a power of 2 is optimized to right shifting which produces errors for negative dividends for all signed types (short, integer, longint) when dividend is not a multiple of divisor (quick fix applied in v7.00 at the cost of optimization)
Here's an example:
var x,y:short;
begin
x:=-3;
asm nop end; // prevents compile time calculations (which are done correctly)
// x=-3 x=-99 x=-1
y:=x/2; // y=-2 y=-50 y=-1 wrong!
y:=-(-x/2); // y=-1 y=-49 y=0 fine (as a positive number is being shifted here)
This error is due to optimizing division by powers of 2 (replacing division with right shifting) while overlooking the fact that negative numbers require additional attention while shifting. Same is true when compiler replaces division by 256 by moving whole bytes (for example, runtime calculations produce -513/256 = -3 !).
The following is no longer true in v 6.61 (except for mC PRO ;)):
In enhanced PIC16 family this error is also 'enhanced' when dividing by 2. In the example above one obtains -3/2=126 and -99/2=78 !.
- negation of HiWord, LoWord produces wrong results when argument is zero or 00FF (fixed in v7.00)
This happens only at compile time when constant values are assigned to variables, like
var wval1,wval2:word;
const dwconst1:dword=0x00FF00FF;
dwconst2:dword=0x00000000;
begin
wval1:=not HiWord(dwconst1); // error - should be 0xFF00, not0x0000
wval1:=not LoWord(dwconst1); // error - should be 0xFF00, not0x0000
wval1:=not HiWord(dwconst2); // error - should be 0xFFFF, not0x00FF
wval1:=not LoWord(dwconst2); // error - should be 0xFFFF, not0x00FF
- in some cases optimizer may fail to notice that part of a variable was changed using Hi, Lo, Higher, or Highest operators (fixed in v7.00).
Problem shows up when one rearranges bytes in multi-byte variable storing intermediate results in a temporary variable.
- while adding bank switching instruction linker may fail to check preceding instruction - which may be a bit-testing one that skips next instruction, like BTFSC (fixed in v7.00)
This happens when optimizer replaces more complicated expression with conditional assembly instructions.
Here's an example:
var i: byte; absolute 0x100;
j: byte; absolute 0x200;
begin
j:=(i and $02) shr 1;
The above expression effectively reduces to j:=i.1 so compiler produces code that tests single bit but, while linking, fails to properly adapt the code when involved variables are located in different banks. In effect, not only the j variable is always cleared but a corresponding place in another memory bank is modified. Resulting assembly demonstrates the issue
MOVLB 2
CLRF _j, 1
MOVLB 1
BTFSC _i, 1, 1
MOVLB 2 <--- may be skipped by preceding instruction
INCF _j, 1, 1
- when one appends constant string to string with '+' operator compiler forgets to add terminator (fixed in v7.00)
An example:
var str1,str2: string[10]
const cstr='def';
str1:='abc';
str2:='12345678'
str2:=str1+cstr; // str2='abcdef78'
- letting compiler extend type in expression may lead to incorrect results (PIC16 and enhanced only) (fixed in v7.00)
Care should be taken in arithmetic expressions where right-side calculations require higher type that the result (left-side variable type). In such case explicitly typecasting the right-side expression to left-side type helps. For example
dim value as longword
dim clk as word
dim m as word
value = 19200
asm nop end asm ' (prevents compile-time calculations)
m = ((Clock_MHz * 1000000) div value)-1 ' m = 159
asm nop end asm
m = word((Clock_MHz * 1000000) div value)-1 ' m = 415
- optimizer may skip code that was supposed to replace division by power of 2 by right-shifting. It happens only when unsigned variable was assigned to a signed one and divisor equals 8 or 16 (fixed in v6.62)
Here's an example:
var int_v: integer;
word_v: word;
begin
uint_v := 1024;
asm nop end; // just to avoid following calculations performed at compile-time
int_v := uint_v; // int_v = 1024
int_v := int_v/8; // no code is generated for this line!
int_v := int_v-100; // int_v = 924!
end.
Effects may be unpredictable if the variables are of different types. In such case residual code
that was supposed to move result of shifting to left-hand variable may produce incidental values.
errors introduced in v6.62 - fixed in v6.63
- functions Hi, Higher, Highest, and HiWord may not function properly at compile time
For example
var dwval: dword;
wval1,wval2: word;
bv1,bv2,bv3,bv4: byte;
begin
dwval:=0x12345678;
wval1:=LoWord(dwval); // wval1=0x5678
wval2:=HiWord(dwval); // wval2=0x5678 !!!
bv1:=Lo(dwval); // bv1=0x78
bv2:=Hi(dwval); // bv2=0x78 !!!
bv3:=Higher(dwval); // bv3=0x78 !!!
bv4:=Highest(dwval); // bv4=0x78 !!!
end.
Processor definition files - vast improvement in v7.00
Before any serious work it is advisable to check processor definition files (placed in Def folder of compiler installation directory) against respective processor datasheet. Unfortunately, these files are not adequately verified and - especially newer ones - may contain errors. Mistakes in *.pas (*.bas) definition files are rare - sometimes declarations of some obscure registers or bits are missing, only occasionally some other mistake appears. Mostly, mistakes are made in *.mlk files which are written in xml format and contain definitions of processor data and program memory structure, interrupt vectors, configuration words, and list of libraries applicable for specific processor.
Besides the old problem with configuration words (written words may differ from read ones due to improper coding of unused bits) and sometimes cryptic naming of specific bits, there may happen (fortunately rare) mistakes in bits representation.
Many mistakes that may have been found in 'bad ram' declarations (unused SFR locations) of newer processors of the enhanced family were removed with release of v7.00. Same about missing 'Mapped Memory' declarations.
Unfortunately, definition files for new processor series, the PIC18FxxK40, do not take into account that compiler adds 0xF00000 to EEPROM address parameter thus overshooting the real address in K40 processors. Solution for the moment is to compensate in respective *.mlk definition file for the automatically added offset by declaring negative EEPROM addresses:
<EEPROM>
<MIN_ADDR>0xFF410000</MIN_ADDR>
<MAX_ADDR>0xFF4103FF</MAX_ADDR>
</EEPROM>
As this was not resolved before mE included newer K-series processors (like K42 or K83)
with similar memory organization, the above applies to these processors, as well.
Code Editor
The following has been collected here just to save others trouble of discovering the same issues or reporting them to mE. mE is aware of the problems listed and certainly will eliminate them with time as already happened with some of the issues (well, maybe not as this was written years ago and the list doesn't change).
- tabs are always replaced by spaces - whatever the settings
- cursor position while marking text close to gutter may be not recognized - actual start
of marking may be the last cursor position
- changes to order of files in Edit window are not saved
- many of Edit Options do not work
- while marking text close to right margin, cursor tends to run away to far right