/* Base address of the data input latch */ volatile unsigned char *baseAddr; /* read parts of output latch */ lsb = *handle->baseAddr; middle = *handle->baseAddr; msb = *handle->baseAddr;
Between reads the bytes are changed in the latch.
Without the volatile, the compiler optimises this to a single assignment:
lsb = middle = msb = *handle->baseAddr;
#define TTYPORT 0x17755U volatile char *port17 = (char)*TTYPORT; *port17 = 'o'; *port17 = 'N';
Without the volatile modifier, the compiler would think that the statement
*port17 = 'o'; is redundant and would remove it from the object
code. The volatile statement prevents the compiler optimisation.Alex
We can try to explain the volatile keyword with an example: Let's say you define a variable var_A and give it an initial value. Later on in function_1() you multiply var_A by 2 an use it for something. Afterwards in function_2() you again multiply var_A by 2 an use it for something else. One possible optimization by the compiler could be to hold var_A in a register and double the value in the register. Then use the (already doubled) register value in func_1() and func_2(). This would save one multiplication! Now let's assume an interrupt occurs exactly between func_1() and func_2() and somewhere in the ISR you change var_A. Then the above optimization would lead to an incorrect result, as var_A is no longer the same for func1/2. The compiler cannot predict when the interrupt is to occur, so it might do the 'wrong' optimization ! In this case (and it's up to you to take care of this!) you use the volatile keyword, to tell the compiler that there might be a chance of changing a variable without notice, and therfore to prevent him from doing such optimizations !