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