Compiler and system libraries
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)
-1 * -128 = 0
-128 * -1 = -128
1 * -128 = 0
-128 * 1 = -128
- results of runtime calculations may differ from compile-time ones
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.
Byte2 := Byte1 mod -1;
If Byte1 is negative, result will be negative at compile-time, but positive
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
if not((bit1=1) and bool_var) then // always evaluates true if bit1=1because 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 not(bool(bit1) and bool_var) then if not bool(PORTB.i) then
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.
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.
procedure test(var bb,bc:byte);
begin
bb:=bc+bb;
End;{test}
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 !).
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
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
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)
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
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.
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.
<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).