/* Fake86: A portable, open-source 8086 PC emulator. Copyright (C)2010-2013 Mike Chambers 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* i8259.c: functions to emulate the Intel 8259 prioritized interrupt controller. note: this is not a very complete 8259 implementation, but for the purposes of a PC, it's all we need. */ #include #include #include "i8259.h" struct structpic i8259; extern uint8_t keyboardwaitack; extern void set_port_write_redirector(uint16_t startport, uint16_t endport, void *callback); extern void set_port_read_redirector(uint16_t startport, uint16_t endport, void *callback); uint8_t in8259(uint16_t portnum) { switch (portnum & 1) { case 0: if (i8259.readmode==0) return(i8259.irr); else return(i8259.isr); case 1: //read mask register return(i8259.imr); } return (0); } extern uint32_t makeupticks; void out8259(uint16_t portnum, uint8_t value) { uint8_t i; switch (portnum & 1) { case 0: if (value & 0x10) { //begin initialization sequence i8259.icwstep = 1; i8259.imr = 0; //clear interrupt mask register i8259.icw[i8259.icwstep++] = value; return; } if ((value & 0x98)==8) { //it's an OCW3 if (value & 2) i8259.readmode = value & 2; } if (value & 0x20) { //EOI command keyboardwaitack = 0; for (i=0; i<8; i++) if ((i8259.isr >> i) & 1) { i8259.isr ^= (1 << i); if ((i==0) && (makeupticks>0)) { makeupticks = 0; i8259.irr |= 1; } return; } } break; case 1: if ((i8259.icwstep==3) && (i8259.icw[1] & 2)) i8259.icwstep = 4; //single mode, so don't read ICW3 if (i8259.icwstep<5) { i8259.icw[i8259.icwstep++] = value; return; } //if we get to this point, this is just a new IMR value i8259.imr = value; break; } } uint8_t nextintr() { uint8_t i, tmpirr; tmpirr = i8259.irr & (~i8259.imr); //XOR request register with inverted mask register for (i=0; i<8; i++) if ((tmpirr >> i) & 1) { i8259.irr ^= (1 << i); i8259.isr |= (1 << i); return(i8259.icw[2] + i); } return(0); //this won't be reached, but without it the compiler gives a warning } void doirq(uint8_t irqnum) { i8259.irr |= (1 << irqnum); if (irqnum == 1) keyboardwaitack = 1; } void init8259() { memset((void *)&i8259, 0, sizeof(i8259)); set_port_write_redirector(0x20, 0x21, &out8259); set_port_read_redirector(0x20, 0x21, &in8259); }