Files
dosemu2/src/base/core/vint.c
geos_one 91736529d5
Some checks failed
Master / Scheduled (FULL) (push) Has been cancelled
Master / Triggered (push) Has been cancelled
Master / Triggered (ASAN) (push) Has been cancelled
Master / Triggered (FULL) (push) Has been cancelled
New upstream version 2.0pre9.2
2025-08-10 12:35:43 +02:00

182 lines
4.4 KiB
C

/*
* 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 <stdint.h>
#include <assert.h>
#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;
}