/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Purpose: virtual interrupt router (another one) * * Author: Stas Sergeev. * */ #include #include #include "cpu.h" #include "int.h" #include "hlt.h" #include "memory.h" #include "port.h" #include "chipset.h" #include "emu.h" #include "vint.h" #define VINT_MAX 2 static int vi_used; static uint16_t vint_hlt; #define ON_PIC1(n) (vih[n].orig_irq >= 8) struct vihandler { void (*handler)(int, int); void (*mask)(int, int); uint8_t irq; uint8_t orig_irq; uint8_t interrupt; int tweaked; unsigned tw_flags; }; struct vihandler vih[VINT_MAX]; static void poll_pic0(uint8_t irq) { port_outb(0x20, 0x0c); // OCW3, enter poll mode port_outb(0x20, irq); // extension, may not work on real PIC /* see if it worked */ assert(pic_get_isr() & (1 << irq)); } static void poll_pic1(uint8_t irq) { port_outb(0x20, 0x0c); // OCW3, enter poll mode port_outb(0x20, 2); // extension, may not work on real PIC port_outb(0xa0, 0x0c); port_outb(0xa0, irq - 8); /* see if it worked */ assert((pic_get_isr() & ((1 << irq) | 4)) == ((1 << irq) | 4)); } static void full_eoi(void) { port_outb(0xa0, 0x20); port_outb(0x20, 0x20); } static void do_ret(int vi_num) { clear_IF(); vih[vi_num].mask(vi_num, 0); do_iret(); } int vint_is_masked(int vi_num, uint8_t *imr) { uint16_t real_imr = (imr[1] << 8) | imr[0]; return !!(real_imr & (1 << vih[vi_num].orig_irq)); } static void vint_handler(uint16_t idx, HLT_ARG(arg)) { uint8_t imr[2]; int masked; int vi_num = idx >> 1; if (idx & 1) { do_ret(vi_num); return; } imr[0] = port_inb(0x21); imr[1] = port_inb(0xa1); masked = vint_is_masked(vi_num, imr); if (masked) { h_printf("vint: masked, iret\n"); do_eoi2_iret(); } else { uint8_t irq = vih[vi_num].orig_irq; uint16_t port = (irq >= 8 ? PIC1_VECBASE_PORT : PIC0_VECBASE_PORT); uint8_t inum = port_inb(port) + (irq & 7); full_eoi(); if (ON_PIC1(vi_num)) poll_pic1(irq); else poll_pic0(irq); if (vih[vi_num].tweaked) { _IP++; // skip hlt h_printf("vint: call to inum %x\n", inum); real_run_int(inum); vih[vi_num].mask(vi_num, 1); } else { h_printf("vint: jump to inum %x\n", inum); jmp_to(ISEG(inum), IOFF(inum)); } } if (vih[vi_num].handler) vih[vi_num].handler(vi_num, masked); } void vint_post_irq_dpmi(int vi_num, int masked) { full_eoi(); if (!masked) { uint8_t irq = vih[vi_num].orig_irq; if (ON_PIC1(vi_num)) poll_pic1(irq); else poll_pic0(irq); } } void vint_init(void) { emu_hlt_t hlt_hdlr = HLT_INITIALIZER; hlt_hdlr.name = "vint"; hlt_hdlr.func = vint_handler; hlt_hdlr.len = VINT_MAX * 2; vint_hlt = hlt_register_handler_vm86(hlt_hdlr); } void vint_setup(void) { int i; for (i = 0; i < VINT_MAX; i++) { if (vih[i].interrupt) SETIVEC(vih[i].interrupt, BIOS_HLT_BLK_SEG, vint_hlt + 2 * i); } } int vint_register(void (*ack_handler)(int, int), void (*mask_handler)(int, int), int irq, int orig_irq, int inum) { struct vihandler *vi = &vih[vi_used]; assert(vi_used < VINT_MAX); vi->handler = ack_handler; vi->mask = mask_handler; vi->irq = irq; vi->orig_irq = orig_irq; vi->interrupt = inum; return vi_used++; } void vint_set_tweaked(int vi_num, int on, unsigned flags) { struct vihandler *vi = &vih[vi_num]; assert(vi_num < VINT_MAX); vi->tweaked = on; vi->tw_flags = flags; }