2 Implementation of IRQ routines on DOS
3 Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <sys/nearptr.h>
28 #include "mikmod.h" /* for MikMod_malloc() & co */
30 unsigned int __irq_stack_size = 0x4000;
31 unsigned int __irq_stack_count = 1;
33 static void __int_stub_template (void)
41 " movw $0x1234,%ax\n" /* Get DPMI data selector */
42 " movw %ax,%ds\n" /* Set DS and ES to data selector */
44 " movl $0x12345678,%ebx\n" /* Interrupt stack top */
47 " subl $0x12345678,%ecx\n" /* Subtract irq_stack_count */
49 " movw %ss,%si\n" /* Save old SS:ESP */
51 " movl %edx,%esp\n" /* Set SS:ESP to interrupt stack */
57 " call 1f\n" /* Call user interrupt handler */
63 " movl %edi,%esp\n" /* Restore old SS:ESP */
76 static int _allocate_iret_wrapper(_go32_dpmi_seginfo * info)
78 unsigned char *irqtpl = (unsigned char *)__int_stub_template;
79 unsigned char *irqend, *irqwrapper, *tmp;
80 __dpmi_meminfo handler_info;
81 unsigned int wrappersize;
83 /* First, skip until pushal */
84 while (*irqtpl != 0x60)
86 /* Now find the iret */
88 while (*irqend++ != 0xcf);
90 wrappersize = 4 + __irq_stack_size * __irq_stack_count + 4 +
91 ((long)irqend - (long)irqtpl);
92 irqwrapper = (unsigned char *) MikMod_malloc(wrappersize);
93 /* Lock the wrapper */
94 handler_info.address = __djgpp_base_address + (unsigned long)irqwrapper;
95 handler_info.size = wrappersize;
96 if (__dpmi_lock_linear_region(&handler_info)) {
97 MikMod_free(irqwrapper);
101 /* First comes the interrupt wrapper size */
102 *(unsigned long *)irqwrapper = wrappersize;
104 /* Next comes the interrupt stack */
105 tmp = irqwrapper + 4 + __irq_stack_size * __irq_stack_count;
107 /* The following dword is interrupt stack pointer */
108 *((void **)tmp) = tmp;
111 /* Now comes the interrupt wrapper itself */
112 memcpy(tmp, irqtpl, irqend - irqtpl);
113 *(unsigned short *)(tmp + 9) = _my_ds();
114 *(unsigned long *)(tmp + 16) = (unsigned long)tmp - 4;
115 *(unsigned long *)(tmp + 26) = __irq_stack_size;
116 *(unsigned long *)(tmp + 46) =
117 info->pm_offset - (unsigned long)(tmp + 50);
119 info->pm_offset = (unsigned long)tmp;
120 info->pm_selector = _my_cs();
125 static void _free_iret_wrapper(_go32_dpmi_seginfo * info)
127 __dpmi_meminfo handler_info;
129 info->pm_offset -= 4 + __irq_stack_size * __irq_stack_count + 4;
131 handler_info.address = __djgpp_base_address + info->pm_offset;
132 handler_info.size = *(unsigned long *)info->pm_offset;
133 __dpmi_unlock_linear_region(&handler_info);
135 MikMod_free((void *)info->pm_offset);
138 struct irq_handle *irq_hook(int irqno, void (*handler)(), unsigned long size)
141 struct irq_handle *irq;
142 __dpmi_version_ret version;
143 __dpmi_meminfo handler_info, struct_info;
144 _go32_dpmi_seginfo info;
145 unsigned long old_sel, old_ofs;
147 __dpmi_get_version(&version);
149 interrupt = version.master_pic + irqno;
151 interrupt = version.slave_pic + (irqno - 8);
153 if (_go32_dpmi_get_protected_mode_interrupt_vector(interrupt, &info))
156 old_sel = info.pm_selector;
157 old_ofs = info.pm_offset;
159 info.pm_offset = (unsigned long)handler;
160 if (_allocate_iret_wrapper(&info))
163 /* Lock the interrupt handler in memory */
164 handler_info.address = __djgpp_base_address + (unsigned long)handler;
165 handler_info.size = size;
166 if (__dpmi_lock_linear_region(&handler_info)) {
167 _free_iret_wrapper(&info);
171 irq = (struct irq_handle *) MikMod_malloc(sizeof(struct irq_handle));
172 irq->c_handler = handler;
173 irq->handler_size = size;
174 irq->handler = info.pm_offset;
175 irq->prev_selector = old_sel;
176 irq->prev_offset = old_ofs;
177 irq->int_num = interrupt;
178 irq->irq_num = irqno;
179 irq->pic_base = irqno < 8 ? PIC1_BASE : PIC2_BASE;
181 struct_info.address = __djgpp_base_address + (unsigned long)irq;
182 struct_info.size = sizeof(struct irq_handle);
183 if (__dpmi_lock_linear_region(&struct_info)) {
185 __dpmi_unlock_linear_region(&handler_info);
186 _free_iret_wrapper(&info);
190 _go32_dpmi_set_protected_mode_interrupt_vector(interrupt, &info);
192 irq->pic_mask = irq_state(irq);
196 void irq_unhook(struct irq_handle *irq)
198 _go32_dpmi_seginfo info;
199 __dpmi_meminfo mem_info;
204 /* Restore the interrupt vector */
206 info.pm_offset = irq->prev_offset;
207 info.pm_selector = irq->prev_selector;
208 _go32_dpmi_set_protected_mode_interrupt_vector(irq->int_num, &info);
210 /* Unlock the interrupt handler */
211 mem_info.address = __djgpp_base_address + (unsigned long)irq->c_handler;
212 mem_info.size = irq->handler_size;
213 __dpmi_unlock_linear_region(&mem_info);
215 /* Unlock the irq_handle structure */
216 mem_info.address = __djgpp_base_address + (unsigned long)irq;
217 mem_info.size = sizeof(struct irq_handle);
218 __dpmi_unlock_linear_region(&mem_info);
220 info.pm_offset = irq->handler;
221 _free_iret_wrapper(&info);
223 /* If IRQ was enabled before we hooked, restore enabled state */
232 /*---------------------------------------------- IRQ detection mechanism -----*/
233 static struct irq_handle *__irqs[16];
234 static int (*__irq_confirm) (int irqno);
235 static volatile unsigned int __irq_mask;
236 static volatile unsigned int __irq_count[16];
238 #define DECLARE_IRQ_HANDLER(irqno) \
239 static void __irq##irqno##_handler () \
241 if (irq_check (__irqs [irqno]) && __irq_confirm (irqno)) \
243 __irq_count [irqno]++; \
244 __irq_mask |= (1 << irqno); \
246 irq_ack (__irqs [irqno]); \
250 DECLARE_IRQ_HANDLER(0)
251 DECLARE_IRQ_HANDLER(1)
252 DECLARE_IRQ_HANDLER(2)
253 DECLARE_IRQ_HANDLER(3)
254 DECLARE_IRQ_HANDLER(4)
255 DECLARE_IRQ_HANDLER(5)
256 DECLARE_IRQ_HANDLER(6)
257 DECLARE_IRQ_HANDLER(7)
258 DECLARE_IRQ_HANDLER(8)
259 DECLARE_IRQ_HANDLER(9)
260 DECLARE_IRQ_HANDLER(10)
261 DECLARE_IRQ_HANDLER(11)
262 DECLARE_IRQ_HANDLER(12)
263 DECLARE_IRQ_HANDLER(13)
264 DECLARE_IRQ_HANDLER(14)
265 DECLARE_IRQ_HANDLER(15)
268 static void (*__irq_handlers[16]) () = {
269 __irq0_handler, __irq1_handler, __irq2_handler, __irq3_handler,
270 __irq4_handler, __irq5_handler, __irq6_handler, __irq7_handler,
271 __irq8_handler, __irq9_handler, __irq10_handler, __irq11_handler,
272 __irq12_handler, __irq13_handler, __irq14_handler, __irq15_handler};
274 void irq_detect_start(unsigned int irqs, int (*irq_confirm) (int irqno))
279 __irq_confirm = irq_confirm;
280 memset(&__irqs, 0, sizeof(__irqs));
281 memset((void *) &__irq_count, 0, sizeof(__irq_count));
283 /* Hook all specified IRQs */
284 for (i = 1; i <= 15; i++)
285 if (irqs & (1 << i)) {
286 __irqs[i] = irq_hook(i, __irq_handlers[i], 200);
287 /* Enable the interrupt */
288 irq_enable(__irqs[i]);
290 /* Enable IRQ2 if we need at least one IRQ above 7 */
295 void irq_detect_end()
298 for (i = 15; i >= 1; i--)
300 irq_unhook(__irqs[i]);
303 int irq_detect_get(int irqno, unsigned int *irqmask)
305 int oldirq = disable();
306 int count = __irq_count[irqno];
307 *irqmask = __irq_mask;
314 void irq_detect_clear()
316 int oldirq = disable();
317 memset((void *) &__irq_count, 0, sizeof(__irq_count));