/* Implementation of IRQ routines on DOS -- from libMikMod. Copyright (C) 1999 by Andrew Zabolotny, This file is part of WildMIDI. WildMIDI is free software: you can redistribute and/or modify the player under the terms of the GNU General Public License and you can redistribute and/or modify the library under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the licenses, or(at your option) any later version. WildMIDI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License and the GNU Lesser General Public License along with WildMIDI. If not, see . */ #include "dosirq.h" #include #include #include #include #include #include unsigned int __irq_stack_size = 0x4000; unsigned int __irq_stack_count = 1; static void __int_stub_template (void) { /* *INDENT-OFF* */ asm(" pushal\n" " pushl %ds\n" " pushl %es\n" " pushl %fs\n" " pushl %gs\n" " movw $0x1234,%ax\n" /* Get DPMI data selector */ " movw %ax,%ds\n" /* Set DS and ES to data selector */ " movw %ax,%es\n" " movl $0x12345678,%ebx\n" /* Interrupt stack top */ " movl (%ebx),%ecx\n" " movl %ecx,%edx\n" " subl $0x12345678,%ecx\n" /* Subtract irq_stack_count */ " movl %ecx,(%ebx)\n" " movw %ss,%si\n" /* Save old SS:ESP */ " movl %esp,%edi\n" " movl %edx,%esp\n" /* Set SS:ESP to interrupt stack */ " movw %ax,%ss\n" " pushl %esi\n" " pushl %edi\n" " pushl %ebx\n" " pushl %edx\n" " call 1f\n" /* Call user interrupt handler */ "1: popl %edx\n" " popl %ebx\n" " movl %edx,(%ebx)\n" " popl %edi\n" " popl %esi\n" " movl %edi,%esp\n" /* Restore old SS:ESP */ " movw %si,%ss\n" " popl %gs\n" " popl %fs\n" " popl %es\n" " popl %ds\n" " popal\n" " iret\n"); /* *INDENT-ON* */ } #include static int _allocate_iret_wrapper(_go32_dpmi_seginfo * info) { unsigned char *irqtpl = (unsigned char *)__int_stub_template; unsigned char *irqend, *irqwrapper, *tmp; __dpmi_meminfo handler_info; unsigned int wrappersize; /* First, skip until pushal */ while (*irqtpl != 0x60) irqtpl++; /* Now find the iret */ irqend = irqtpl; while (*irqend++ != 0xcf); wrappersize = 4 + __irq_stack_size * __irq_stack_count + 4 + ((long)irqend - (long)irqtpl); irqwrapper = (unsigned char *) malloc(wrappersize); /* Lock the wrapper */ handler_info.address = __djgpp_base_address + (unsigned long)irqwrapper; handler_info.size = wrappersize; if (__dpmi_lock_linear_region(&handler_info)) { free(irqwrapper); return -1; } /* First comes the interrupt wrapper size */ *(unsigned long *)irqwrapper = wrappersize; /* Next comes the interrupt stack */ tmp = irqwrapper + 4 + __irq_stack_size * __irq_stack_count; /* The following dword is interrupt stack pointer */ *((void **)tmp) = tmp; tmp += 4; /* Now comes the interrupt wrapper itself */ memcpy(tmp, irqtpl, irqend - irqtpl); *(unsigned short *)(tmp + 9) = _my_ds(); *(unsigned long *)(tmp + 16) = (unsigned long)tmp - 4; *(unsigned long *)(tmp + 26) = __irq_stack_size; *(unsigned long *)(tmp + 46) = info->pm_offset - (unsigned long)(tmp + 50); info->pm_offset = (unsigned long)tmp; info->pm_selector = _my_cs(); return 0; } static void _free_iret_wrapper(_go32_dpmi_seginfo * info) { __dpmi_meminfo handler_info; info->pm_offset -= 4 + __irq_stack_size * __irq_stack_count + 4; handler_info.address = __djgpp_base_address + info->pm_offset; handler_info.size = *(unsigned long *)info->pm_offset; __dpmi_unlock_linear_region(&handler_info); free((void *)info->pm_offset); } struct irq_handle *irq_hook(int irqno, void (*handler)(), unsigned long size) { int interrupt; struct irq_handle *irq; __dpmi_version_ret version; __dpmi_meminfo handler_info, struct_info; _go32_dpmi_seginfo info; unsigned long old_sel, old_ofs; __dpmi_get_version(&version); if (irqno < 8) interrupt = version.master_pic + irqno; else interrupt = version.slave_pic + (irqno - 8); if (_go32_dpmi_get_protected_mode_interrupt_vector(interrupt, &info)) return NULL; old_sel = info.pm_selector; old_ofs = info.pm_offset; info.pm_offset = (unsigned long)handler; if (_allocate_iret_wrapper(&info)) return NULL; /* Lock the interrupt handler in memory */ handler_info.address = __djgpp_base_address + (unsigned long)handler; handler_info.size = size; if (__dpmi_lock_linear_region(&handler_info)) { _free_iret_wrapper(&info); return NULL; } irq = (struct irq_handle *) malloc(sizeof(struct irq_handle)); irq->c_handler = handler; irq->handler_size = size; irq->handler = info.pm_offset; irq->prev_selector = old_sel; irq->prev_offset = old_ofs; irq->int_num = interrupt; irq->irq_num = irqno; irq->pic_base = irqno < 8 ? PIC1_BASE : PIC2_BASE; struct_info.address = __djgpp_base_address + (unsigned long)irq; struct_info.size = sizeof(struct irq_handle); if (__dpmi_lock_linear_region(&struct_info)) { free(irq); __dpmi_unlock_linear_region(&handler_info); _free_iret_wrapper(&info); return NULL; } _go32_dpmi_set_protected_mode_interrupt_vector(interrupt, &info); irq->pic_mask = irq_state(irq); return irq; } void irq_unhook(struct irq_handle *irq) { _go32_dpmi_seginfo info; __dpmi_meminfo mem_info; if (!irq) return; /* Restore the interrupt vector */ irq_disable(irq); info.pm_offset = irq->prev_offset; info.pm_selector = irq->prev_selector; _go32_dpmi_set_protected_mode_interrupt_vector(irq->int_num, &info); /* Unlock the interrupt handler */ mem_info.address = __djgpp_base_address + (unsigned long)irq->c_handler; mem_info.size = irq->handler_size; __dpmi_unlock_linear_region(&mem_info); /* Unlock the irq_handle structure */ mem_info.address = __djgpp_base_address + (unsigned long)irq; mem_info.size = sizeof(struct irq_handle); __dpmi_unlock_linear_region(&mem_info); info.pm_offset = irq->handler; _free_iret_wrapper(&info); /* If IRQ was enabled before we hooked, restore enabled state */ if (irq->pic_mask) irq_enable(irq); else irq_disable(irq); free(irq); } /*---------------------------------------------- IRQ detection mechanism -----*/ static struct irq_handle *__irqs[16]; static int (*__irq_confirm) (int irqno); static volatile unsigned int __irq_mask; static volatile unsigned int __irq_count[16]; #define DECLARE_IRQ_HANDLER(irqno) \ static void __irq##irqno##_handler () \ { \ if (irq_check (__irqs [irqno]) && __irq_confirm (irqno)) \ { \ __irq_count [irqno]++; \ __irq_mask |= (1 << irqno); \ } \ irq_ack (__irqs [irqno]); \ } /* *INDENT-OFF* */ DECLARE_IRQ_HANDLER(0) DECLARE_IRQ_HANDLER(1) DECLARE_IRQ_HANDLER(2) DECLARE_IRQ_HANDLER(3) DECLARE_IRQ_HANDLER(4) DECLARE_IRQ_HANDLER(5) DECLARE_IRQ_HANDLER(6) DECLARE_IRQ_HANDLER(7) DECLARE_IRQ_HANDLER(8) DECLARE_IRQ_HANDLER(9) DECLARE_IRQ_HANDLER(10) DECLARE_IRQ_HANDLER(11) DECLARE_IRQ_HANDLER(12) DECLARE_IRQ_HANDLER(13) DECLARE_IRQ_HANDLER(14) DECLARE_IRQ_HANDLER(15) /* *INDENT-ON* */ static void (*__irq_handlers[16]) () = { __irq0_handler, __irq1_handler, __irq2_handler, __irq3_handler, __irq4_handler, __irq5_handler, __irq6_handler, __irq7_handler, __irq8_handler, __irq9_handler, __irq10_handler, __irq11_handler, __irq12_handler, __irq13_handler, __irq14_handler, __irq15_handler}; void irq_detect_start(unsigned int irqs, int (*irq_confirm) (int irqno)) { int i; __irq_mask = 0; __irq_confirm = irq_confirm; memset(__irqs, 0, sizeof(__irqs)); memset((void *) __irq_count, 0, sizeof(__irq_count)); /* Hook all specified IRQs */ for (i = 1; i <= 15; i++) if (irqs & (1 << i)) { __irqs[i] = irq_hook(i, __irq_handlers[i], 200); /* Enable the interrupt */ irq_enable(__irqs[i]); } /* Enable IRQ2 if we need at least one IRQ above 7 */ if (irqs & 0xff00) _irq_enable(2); } void irq_detect_end() { int i; for (i = 15; i >= 1; i--) if (__irqs[i]) irq_unhook(__irqs[i]); } int irq_detect_get(int irqno, unsigned int *irqmask) { int oldirq = disable(); int count = __irq_count[irqno]; *irqmask = __irq_mask; __irq_mask = 0; if (oldirq) enable(); return count; } void irq_detect_clear() { int oldirq = disable(); memset((void *) __irq_count, 0, sizeof(__irq_count)); __irq_mask = 0; if (oldirq) enable(); }