An Initial C++ Solution
There is no single solution to a problem such as this, so a series of
approaches will be investigated. Here is a first attempt at a
write-only port (wop) class:
class wop
{
int shadow;
// not accessible
int* address; // to user
public:
wop(long);
// constructor
~wop();
// destructor
void or(int);
// "operator"
void and(int); // functions
};
This hides the address of the port itself (address) and the shadow
copy of the data (shadow) in the private part of the class, which can
only be accessed by member functions.
A constructor initializes the member shadow value and the port
itself with an arbitrary initial value (of zero):
wop::wop(long port)
{
address = (int*) port;
shadow = 0; //
initial value
*address = 0;
}
The destructor would probably simply write zero to the port to
return it to its initial state.
Two further member functions provide the user with a means of
performing OR and AND operations on the port, which facilitates setting
and clearing of individual bits:
void wop::or(int val)
{
shadow |= val;
// set
bit(s) in copy
*address = shadow;
// update port
}
void wop::and(int val)
{
shadow &= val;
// clear bit(s) in copy
*address = shadow;
// update port
}
In
both cases, the shadow data is maintained automatically. The
applications programmer can create wop objects and use them thus:
main()
{
wop out(0x10000);
out.or(0x30);
// set bits 4
and 5
out.and(~7);
// clear
bits 0, 1 and 2
};
Overloaded Operators
This C++ solution may be improved in a variety of ways. To start with,
the use of the two member functions to perform OR and AND operations is
not "natural". It would be much better if intuitive operators where
available. This can be accommodated by overloading the |= and &=
operators thus:
class wop
{
int shadow;
int* address;
public:
wop(long);
// constructor
~wop();
// destructor
void operator|=(int); //
overloaded
void operator&=(int); // operators
};
The way these operators perform should be exactly as any C/C++
programmer would expect. They may be implemented like this:
void wop::operator|=(int val)
{
shadow |= val;
// set
bit(s) in copy
*address =
shadow; // update port
}
void wop::operator&=(int val)
{
shadow &= val;
// clear bit(s) in copy
*address = shadow; //
update port
}
Interestingly, this code is identical to the previous member
functions, with just a change of function names. The new operators may
be used thus:
main()
{
wop
out(0x10000);
out |= 0x30;
// set bits 4 and 5
out &= ~7;
// clear bits 0, 1 and 2
}
<>
Initialization
With the wop class above, there is a rational strategy for
initialization " zero is written to the port (and saved in the shadow).
However, it may be useful to allow the user to specify a custom value
for each object. This requires a small change to the class definition
and the constructor function, thus:
class wop
{
int
shadow;
int* address;
int
initval;
// stored initial value
public:
wop(long, int);
// constructor
~wop();
// destructor
void operator|=(int);
// overloaded
void operator&=(int);
// operators
};
>
wop::wop(long port, int init=0)
{
address = (int*) port;
initval = init;
shadow = initval; // initial value
*address = initval;
}
The constructor takes an additional parameter, which specifies the
initialization value, which may be applied like this:
main()
{
wop
out(0x10000, 0x0f);
out |= 0x30;
// set bits 4
and 5
out &= ~7;
// clear bits 0, 1 and 2
}
Note that this second parameter has a default value of zero, which
would be used if the constructor were called with a single parameter.
This provides useful backwards compatibility with the previous version
of the wop class.
The initial value is also retained in a private member variable
(initval) which might be utilized by a destructor:
wop::~wop()
{
*address = initval;
// shutdown
value
}