Listing 3: Splitting device and interrupt controller management into two classes

// A uniform framework for device interrupt handlers.
class irq_handler_c {
public: virtual void isr (void) {return;}
};

// Code to manage a hypothetical interrupt
// controller chip, through which a device
// of type device_c and others is connected.
class irq_controller_c {
private:
  // Table of device interrupt handlers.
  static irq_handler_c* device_table[];
  
  // Memory address of the controller chip.
  volatile static char* irqaddr;

  // Commands recognized by the controller chip.
  enum {ACK = 0, EN = 1, DIS = 2};

public:

  // The controller's interrupt handler.  The microprocessor gives
  // control to this function whenever any device connected to the
  // controller issues an interrupt request.
  static void isr (void);

  irq_controller_c (char* irqioaddr) {irqaddr = irqioaddr;};

  // When a device handler wants interrupt requests for a device,
  // it registers with us using this function.
  void register_handler (int irq, irq_handler_c* handler)
  {
    device_table[irq] = handler;
    return;
  }
};

#pragma interrupt
void irq_controller_c::isr (void)
{
  int irq;

  // Tell the controller we're starting interrupt processing.
  *irqaddr = ACK;

  // determine which device is reqesting service
  irq = *irqaddr;

  // Invoke the interrupt handler for the requesting device.
  device_table[irq]->isr(irq);

  // We're done, reenable the interrupt request line.
  *irqaddr = EN;

  return;
}

// The interrupt controller chip has 16 request lines.
irq_handler_c* irq_controller_c::device_table[16];

// The reimplemented device_c class, with interrupt
// controller management-related code removed since
// that responsibility now goes to irq_controller_c.
class device_c : public irq_handler_c {
private:
  fifo_c txbuf;
  enum {TR = 0, SR = 1, CR = 2};
  enum {SR_TXI = 1, CR_TXE = 1};
  volatile static char* devaddr;
  int interrupts;

public:
  void isr (int irq);
  device_c (char* devioaddr) {devaddr = devioaddr;}
  void write (char c) {txbuf.put(c); devaddr[CR] |= CR_TXE;}
};

void device_c::isr (void)
{
  int c;

  interrupts++;
  while (devaddr[SR]) {
    if (devaddr[SR] && SR_TXI) {
      if ((c = txbuf.get()) != -1 )
        devaddr[TR] = c;
    }
    // ...
  }
  return;
}

extern void (*interrupt_vector_table[])();

// The irq controller chip is tied to entry 100
// of the host microprocessor's interrupt vector table.
// The controller chip is located at address 0xfffffe00.
#define IRQ_CONTROLLER 100
irq_controller_c irqctl((char*)0xfffffe00);

// The serial device is tied to pin 1 of the interrupt controller,
// and is located at address 0xffff0000.
#define IRQ_DEVICE 1
device_c serial1((char*)0xffff0000);

int main ( void )
{
  const char* hello = "hello, world!\n";
  const char* hellop =  hello;
  
  irqctl.register_handler(IRQ_DEVICE, &serial1);
  interrupt_vector_table[IRQ_CONTROLLER] = irqctl.isr;

  while (*hellop) serial1.write(*hellop++);
  return 0;
}
— End of Listing —