Internal representation of variables
Multi-byte variables are stored in RAM as little endian, i.e. least significant byte is first, followed by more significant ones. Mostly, one does not need to concern oneself with it as there are operators allowing to access single bytes of a muli-byte variable (Lo, Hi, Higher, and Highest).
Arrays are stored by elements, with multi-dimensional arrays most inner dimension elements stored first (most inner are the ones indexed by the latest index, i.e. in arr[j][i], i is the index pointing to most inner elements). For example, elements of an array declared as
arr: array[2] of array[3] of byte;
are stored in following order
[0][0] [0][1] [0][2] [1][0] [1][1] [1][2]
Strings and arrays of characters are stored in the order of their elements (characters) with added termination char equal zero. This means that both string[10] and array[10] of char need 11 bytes of RAM. If a string assigned to string variable (array of char) is shorter than the declared variable size, then terminating char will follow last assigned character. For example
st:='abc';results in
st[0]='a', at[1]='b', st[3]='c', st[4]=0If, instead of using library routines, one manipulates string elements directly, one should always ascertain that terminating char is correctly placed.
x = +/-m * 2^exp
where m is a rational mantissa (significand) and exp - an integer exponent. In Microchip format there are 8 bits of exponent, one sign bit and 23 bits of mantissa (actually, mantissa is 24 bits wide with assumption that first bit is always 1 and so it may be skipped - this only means that mantissa is in the range from 1 to 2, and not 0 to 1 - unlike in IEEE 754, numbers with most significant bit equal 0 are not used in Microchip format):
bit # 31 23 22 0
| Exponent |S| Mantissa |
1.d1 ... d23 (binary rational number)
Exponent ranges from -126 to 128. It's offset by 127, so 1.0 is represented with 7F 00 00 00.Lo(x) -> least significant byte of mantissa (bits d16..d23) Hi(x) -> less significant byte of mantissa (bits d8..d15) Higher(x) or $80 -> most significant byte of mantissa (1.d1d2d3d4d5d6d7) Higher(x).7 -> sign bit (0 - positive, 1 - negative) Highest(x) -> exponent, offset by 127For example, number 12.345 is coded in Micochip format as hex 82 45 85 1F (exponent is hex 82, or decimal 130, sign bit is 0, rest is mantissa without first bit) or, in binary, as 10000010 01000101 10000101 00011111 (naturally, in RAM the order of bytes will be reversed). This represents a binary rational number
1.10001011000010100011111 x 2^3
(as 130-127=3, and first mantissa bit is always 1) or
1100.01011000010100011111
EEPROM_Write(Address,Lo(x)); EEPROM_Write(Address+1,Hi(x)); EEPROM_Write(Address+2,Higher(x)); EEPROM_Write(Address+3,Highest(x)); Lo(x):=EEPROM_Read(Address); Hi(x):=EEPROM_Read(Address+1); Higher(x):=EEPROM_Read(Address+2); Highest(x):=EEPROM_Read(Address+3);One may also map an array of bytes on the variable x
var x: real;
ax: array[4] of byte at x;
Any operation on elements of ax will be effectively performed on x, like
for i:=0 to 3 do EEPROM_Write(Address+i,ax[i]); for i:=0 to 3 do ax[i]:=EEPROM_Read(Address+i);And finally, one may declare a pointer to byte initialised to point at x:
var x: real;
px: ^byte;
px:=^byte(@x);
for i:=1 to 4 do
begin
EEPROM_Write(Address,px^);
inc(px);
inc(Address);
end;
i:=0;
while st[i]<>0 do
begin
st[i]:=' ';
inc(i);
end;
one may use
FSR0ptr:=@st; while INDF0<>0 do POSTINC0:=' ';Though compiler also uses indirect addressing, the latter code is twice smaller and over three times faster. PIC18 processors have three FSR registers. In mikroPascal, FSR0, FSR1 and FSR2 are declared in processor definition files as word variables, while FSR0ptr, FSR1ptr, and FSR2ptr are declared as pointers to byte, but they represent the same registers so following assignments are equivalent:
FSR0ptr:=@st; FSR0:=word(@st);Indirect File Operands may be used on both sides of an assignment, like, for example
POSTINC0:=POSTDEC1;After the assignment is executed, besides copying a byte pointed to by FSR1 to memory pointed to by FSR0, FSR0 will be incremented, while FSR1 will be decremented.
procedure FSRcontext;
begin
asm
movf FSR0L,F
movf FSR1L,F
movf FSR2L,F
end;
End;{FSRcontext}
program Reentrancy;
type Tproc = procedure(dr:byte); // procedure type identical to PWMx_Set_Duty
var bb1,bb2: byte;
procedure PWM_dummy(dr:byte); forward; // intermediate procedures
procedure PWM_main(dr:byte); forward; // allowing to call PWM1_Set_Duty
procedure PWM_inter(dr:byte); forward; // both from main and ISR
procedure interrupt;
begin
PWM_inter(127); // set PWM1 to 127
End;{interrupt}
procedure PWM_dummy(dr:byte);
begin
dr:=dr; // to avoid compiler hint
End;{PWM_dummy}
procedure PWM_main(dr:byte); // to be called from main program
var ProcPtr: ^Tproc;
S_INTCON: byte;
begin
SetFuncCall(PWM1_Set_Duty); // not really necessary
ProcPtr:=@PWM1_Set_Duty;
S_INTCON:=INTCON.GIE; // main code may call PWM1_Set_Duty only with
INTCON.GIE:=0; // interrupts blocked
ProcPtr^(dr);
INTCON.GIE:=S_INTCON;
End;{PWM_main}
procedure PWM_inter(dr:byte); // to be called from ISR
var ProcPtr: ^Tproc;
begin
SetFuncCall(PWM_dummy); // make linker believe PWM_dummy will be called
ProcPtr:=@PWM1_Set_Duty; // while calling PWM1_Set_Duty from ISR
ProcPtr^(dr);
End;{PWM_inter}
BEGIN
PWM1_Init(5000); // PWM initialisation
PWM1_Start;
PWM_main(0); // set PWM1 to 0
while True do
begin
// main program loop
end;
END.
As you see, there are additional three procedures declared - a dummy one used to cheat the linker, and two procedures that call PWM1_Set_Duty via pointers. One of the latter may be called from ISR, the other from main part of code.