doompanning/src/dp_doom/external/wildmidi-0.4.5/djgpp/dosirq.c

323 lines
8.5 KiB
C
Raw Normal View History

/* Implementation of IRQ routines on DOS -- from libMikMod.
Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
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
<http://www.gnu.org/licenses/>.
*/
#include "dosirq.h"
#include <dpmi.h>
#include <go32.h>
#include <dos.h>
#include <sys/nearptr.h>
#include <malloc.h>
#include <string.h>
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 <stdio.h>
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();
}