322 lines
8.5 KiB
C
322 lines
8.5 KiB
C
/* 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();
|
|
}
|