Re: volatile keyword for C++ member functions
On Dec 31, 9:03 am, Rahul <sam_...@yahoo.co.in> wrote:
A competent programmer never makes any assumptions. As to the meaning
of volatile, you can probably trust what your compiler's documentation
tells you. But keep in mind that its original purpose was embedded
programming, where reading from a hardware register twice could return
two different values, both of which were important. That, of course,
has nothing to do with multi-threaded programs.
Could you explain the purpose of volatile in embedded with an
example program?
I'm afraid i didn't get its purpose from your statement, which
seems to be based on pratical experience...
OK. I'll take a very concrete, albeit dated example. (Dated,
because I've not worked in this field for a long time. Note too
that this is all from memory---the exact details are probably
inaccurate, even though the general picture is correct.)
Consider the Intel 8259 serial interface commandler. It's
accessed by means of two "registers", a data register, and a
command/status register; the command/status register is treated
as a command when it is written, and returns the status when it
is read. Imagine that on a certain machine, this commandler is
installed with the registers memory mapped to the addresses
0xFFF00 and 0xFFF02 (or __FAR_PTR( 0xFFF0, 0 ) and __FAR_PTR(
0xFFF0, 2 ), with the command/status register as the first. (On
a typical compiler for the Intel 8086, the macro __FAR_PTR
converted a segment/offset pair to a far pointer.)
To initialize the chip, it is necessary to start by writing 0
three times to the command/status register, followed by a
configuration command (two bytes, I think). In the status
register, there was a bit which indicated that a character had
been received, and an other which indicated that the chip was
ready to send. The code for handling this might look something
like:
unsigned char volatile* const
commandReg = __FAR_PTR( 0xFFF0, 0 ) ;
unsigned char volatile* const
statusReg = __FAR_PTR( 0xFFF0, 0 ) ;
unsigned char volatile* const
dataReg = __FAR_PTR( 0xFFF0, 2 ) ;
// ...
// initialization.
void
initialize8259()
{
*commandReg = 0 ;
*commandReg = 0 ;
*commandReg = 0 ;
*commandReg = firstInitByte ;
*commandReg = secondInitByte ;
}
unsigned char
read8259()
{
while ( (*statusReg & 0x01) == 0 ) {
}
return *dataReg ;
}
void
write8259(
unsigned char ch )
{
while ( (*statusReg & 0x04) == 0 ) {
}
*dataReg = ch ;
}
Note that without the volatile, any decent compiler will notice
that in the initialize8259 routine, you overwrite the same
memory several times without ever reading the value, and so
suppress all of the writes except the last. Similarly, in the
wait loops in read8259 and write8259, the compiler will see that
there is nothing in the loop which can change the value read
from "memory", and so will simply move it into a CPU register
before executing the loop, and test the value there each time
through the loop, rather than rereading the value from "memory"
(which is in fact the register on the 8259 chip) each time in
the loop.
Such busy loops would never be used in a modern OS, of course,
but device controllers have become much more complicated as
well, and it's not at all rare for a byte oriented device to
require outputting (writing) several bytes to the same address
for a single command, or inputting (reading) several bytes from
the same address to get the complete status.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34