X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=libs%2Fmikmod%2Fdrvdos%2Fdosirq.c;fp=libs%2Fmikmod%2Fdrvdos%2Fdosirq.c;h=9b0e21f5f85da17583ccc633f55030dcb534ac8c;hp=0000000000000000000000000000000000000000;hb=77db1ca18d5446dcda9e524261399b63c2cd1813;hpb=a714b8c4811627d874934b0a0387b8cb27fc5921 diff --git a/libs/mikmod/drvdos/dosirq.c b/libs/mikmod/drvdos/dosirq.c new file mode 100644 index 0000000..9b0e21f --- /dev/null +++ b/libs/mikmod/drvdos/dosirq.c @@ -0,0 +1,323 @@ +/* + Implementation of IRQ routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "dosirq.h" + +#include +#include +#include +#include +#include +#include +#include "mikmod.h" /* for MikMod_malloc() & co */ + +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 *) MikMod_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)) { + MikMod_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); + + MikMod_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 *) MikMod_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)) { + MikMod_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); + + MikMod_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(); +} + +/* ex:set ts=4: */