// a fifo implementation of some kind
class fifo_c {
public:
fifo_c();
void put (int c);
int get (void);
};
// Part of a driver class for a hypothetical, memory-mapped,
// interrupt driven serial i/o device tied to the host processor
// through an interrupt controller chip.
class device_c {
private:
// transmit buffer
static fifo_c txbuf;
// register map for the device
enum {TR = 0, SR = 1, CR = 2};
// the lsb of SR is the status of the transmitter
// interrupt request signal; the lsb of CR is the
// transmitter enable.
enum {SR_TXI = 1, CR_TXE = 1};
// the base memory address of the device
volatile static char* devaddr;
// commands for the interrupt controller hardware
enum {ACK=0, EN=1, DIS=2};
// the base memory address of the interrupt controller
volatile static char* irqaddr;
// a this-lookalike, for nonstatic data
static device_c* me;
// nonstatic interrupt counter
int interrupts;
public:
// the device interrupt handler
static void isr (void);
device_c (char* devioaddr, char* irqioaddr)
{
// Save our "this" pointer.
me = this;
// Save the device's hardware address.
devaddr = devioaddr;
// Save the address of the interrupt controller.
irqaddr = irqioaddr;
};
void write (char c)
{
// Stuff the byte into the transmit buffer.
txbuf.put(c);
// Enable the transmitter. The transmit interrupt
// handler will subsequently grab the byte out
// of the buffer and stuff it to the hardware.
devaddr[CR] |= CR_TXE;
}
};
#pragma interrupt
void device_c::isr (void)
{
int c;
// Count the interrupt. The interrupt count
// is non-static, thus we have to use our copied
// "this" pointer to find it.
me->interrupts++;
// Acknowledge the interrupt request
// to the interrupt controller chip.
*irqaddr = ACK;
// Figure out what the interrupt request is, and
// service it. We may have multiple requests
// pending, so try to service them all.
while (devaddr[SR]) {
if (devaddr[SR] && SR_TXI) {
// The transmitter wants another byte,
// give it one if we have one.
if ((c = txbuf.get()) != -1 )
devaddr[TR] = c;
}
// Handle other types of device interrupts here.
// ...
}
// Re-enable interrupt requests through the controller.
*irqaddr = EN;
// If the "interrupt" pragma is supported, then this
// return is an RTE instead of an RTS. If the pragma
// isn't supported then we'll need a wrapper function;
// see the article text for an example.
return;
}
device_c serial1((char*)0xffff0000, (char*)0xffffef00);
extern void (*interrupt_vector_table[])();
#define IRQ_DEVICE 1
int main ( void )
{
const char* hello = "hello, world!\n";
const char* hellop = hello;
interrupt_vector_table[IRQ_DEVICE] = serial1.isr;
while (*hellop) serial1.write(*hellop++);
return 0;
}
End of Listing