aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/pic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev/pic.cc')
-rw-r--r--tools/ioemu/iodev/pic.cc872
1 files changed, 872 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/pic.cc b/tools/ioemu/iodev/pic.cc
new file mode 100644
index 0000000000..d79c376220
--- /dev/null
+++ b/tools/ioemu/iodev/pic.cc
@@ -0,0 +1,872 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pic.cc,v 1.33 2003/08/05 09:19:36 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS thePic->
+
+
+
+bx_pic_c *thePic = NULL;
+
+ int
+libpic_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ thePic = new bx_pic_c ();
+ bx_devices.pluginPicDevice = thePic;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePic, BX_PLUGIN_PIC);
+ return(0); // Success
+}
+
+ void
+libpic_LTX_plugin_fini(void)
+{
+}
+
+
+bx_pic_c::bx_pic_c(void)
+{
+ put("PIC");
+ settype(PICLOG);
+}
+
+bx_pic_c::~bx_pic_c(void)
+{
+ // nothing for now
+}
+
+
+ void
+bx_pic_c::init(void)
+{
+ /* 8259 PIC (Programmable Interrupt Controller) */
+ DEV_register_ioread_handler(this, read_handler, 0x0020, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0021, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x00A0, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x00A1, "8259 PIC", 1);
+
+ DEV_register_iowrite_handler(this, write_handler, 0x0020, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0021, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x00A0, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x00A1, "8259 PIC", 1);
+
+
+ BX_PIC_THIS s.master_pic.single_PIC = 0;
+ BX_PIC_THIS s.master_pic.interrupt_offset = 0x08; /* IRQ0 = INT 0x08 */
+ /* slave PIC connected to IRQ2 of master */
+ BX_PIC_THIS s.master_pic.u.slave_connect_mask = 0x04;
+ BX_PIC_THIS s.master_pic.sfnm = 0; /* normal nested mode */
+ BX_PIC_THIS s.master_pic.buffered_mode = 0; /* unbuffered mode */
+ BX_PIC_THIS s.master_pic.master_slave = 0; /* no meaning, buffered_mode=0 */
+ BX_PIC_THIS s.master_pic.auto_eoi = 0; /* manual EOI from CPU */
+ BX_PIC_THIS s.master_pic.imr = 0xFF; /* all IRQ's initially masked */
+ BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0; /* IRR */
+ BX_PIC_THIS s.master_pic.irq = 0;
+ BX_PIC_THIS s.master_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ BX_PIC_THIS s.master_pic.init.requires_4 = 0;
+ BX_PIC_THIS s.master_pic.init.byte_expected = 0;
+ BX_PIC_THIS s.master_pic.special_mask = 0;
+ BX_PIC_THIS s.master_pic.lowest_priority = 7;
+ BX_PIC_THIS s.master_pic.polled = 0;
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0;
+
+ BX_PIC_THIS s.slave_pic.single_PIC = 0;
+ BX_PIC_THIS s.slave_pic.interrupt_offset = 0x70; /* IRQ8 = INT 0x70 */
+ BX_PIC_THIS s.slave_pic.u.slave_id = 0x02; /* slave PIC connected to IRQ2 of master */
+ BX_PIC_THIS s.slave_pic.sfnm = 0; /* normal nested mode */
+ BX_PIC_THIS s.slave_pic.buffered_mode = 0; /* unbuffered mode */
+ BX_PIC_THIS s.slave_pic.master_slave = 0; /* no meaning, buffered_mode=0 */
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0; /* manual EOI from CPU */
+ BX_PIC_THIS s.slave_pic.imr = 0xFF; /* all IRQ's initially masked */
+ BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0; /* IRR */
+ BX_PIC_THIS s.slave_pic.irq = 0;
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ BX_PIC_THIS s.slave_pic.init.requires_4 = 0;
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 0;
+ BX_PIC_THIS s.slave_pic.special_mask = 0;
+ BX_PIC_THIS s.slave_pic.lowest_priority = 7;
+ BX_PIC_THIS s.slave_pic.polled = 0;
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0;
+
+ for (unsigned i=0; i<8; i++) { /* all IRQ lines low */
+ BX_PIC_THIS s.master_pic.IRQ_line[i] = 0;
+ BX_PIC_THIS s.slave_pic.IRQ_line[i] = 0;
+ }
+}
+
+ void
+bx_pic_c::reset(unsigned type)
+{
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pic_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PIC_SMF
+ bx_pic_c *class_ptr = (bx_pic_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+
+ Bit32u
+bx_pic_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIC_SMF
+
+ BX_DEBUG(("IO read from %04x", (unsigned) address));
+
+ /*
+ 8259A PIC
+ */
+
+ if((address == 0x20 || address == 0x21) && BX_PIC_THIS s.master_pic.polled) {
+ // In polled mode. Treat this as an interrupt acknowledge
+ clear_highest_interrupt(& BX_PIC_THIS s.master_pic);
+ BX_PIC_THIS s.master_pic.polled = 0;
+ service_master_pic();
+ return io_len==1?BX_PIC_THIS s.master_pic.irq:(BX_PIC_THIS s.master_pic.irq)<<8|(BX_PIC_THIS s.master_pic.irq); // Return the current irq requested
+ }
+
+ if((address == 0xa0 || address == 0xa1) && BX_PIC_THIS s.slave_pic.polled) {
+ // In polled mode. Treat this as an interrupt acknowledge
+ clear_highest_interrupt(& BX_PIC_THIS s.slave_pic);
+ BX_PIC_THIS s.slave_pic.polled = 0;
+ service_slave_pic();
+ return io_len==1?BX_PIC_THIS s.slave_pic.irq:(BX_PIC_THIS s.slave_pic.irq)<<8|(BX_PIC_THIS s.slave_pic.irq); // Return the current irq requested
+ }
+
+
+ switch (address) {
+ case 0x20:
+ if (BX_PIC_THIS s.master_pic.read_reg_select) { /* ISR */
+ BX_DEBUG(("read master ISR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.isr));
+ return(BX_PIC_THIS s.master_pic.isr);
+ }
+ else { /* IRR */
+ BX_DEBUG(("read master IRR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.irr));
+ return(BX_PIC_THIS s.master_pic.irr);
+ }
+ break;
+ case 0x21:
+ BX_DEBUG(("read master IMR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.imr));
+ return(BX_PIC_THIS s.master_pic.imr);
+ break;
+ case 0xA0:
+ if (BX_PIC_THIS s.slave_pic.read_reg_select) { /* ISR */
+ BX_DEBUG(("read slave ISR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.isr));
+ return(BX_PIC_THIS s.slave_pic.isr);
+ }
+ else { /* IRR */
+ BX_DEBUG(("read slave IRR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.irr));
+ return(BX_PIC_THIS s.slave_pic.irr);
+ }
+ break;
+ case 0xA1:
+ BX_DEBUG(("read slave IMR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.imr));
+ return(BX_PIC_THIS s.slave_pic.imr);
+ break;
+ }
+
+ BX_PANIC(("io read to address %04x", (unsigned) address));
+ return(0); /* default if not found above */
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pic_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PIC_SMF
+ bx_pic_c *class_ptr = (bx_pic_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_pic_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIC_SMF
+
+ BX_DEBUG(("IO write to %04x = %02x", (unsigned) address, (unsigned) value));
+
+ /*
+ 8259A PIC
+ */
+
+ switch (address) {
+ case 0x20:
+ if (value & 0x10) { /* initialization command 1 */
+ BX_DEBUG(("master: init command 1 found"));
+ BX_DEBUG((" requires 4 = %u", (unsigned) (value & 0x01) ));
+ BX_DEBUG((" cascade mode: [0=cascade,1=single] %u",
+ (unsigned) ((value & 0x02) >> 1)));
+ BX_PIC_THIS s.master_pic.init.in_init = 1;
+ BX_PIC_THIS s.master_pic.init.requires_4 = (value & 0x01);
+ BX_PIC_THIS s.master_pic.init.byte_expected = 2; /* operation command 2 */
+ BX_PIC_THIS s.master_pic.imr = 0x00; /* clear the irq mask register */
+ BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.master_pic.lowest_priority = 7;
+ BX_PIC_THIS s.master_pic.INT = 0; /* reprogramming clears previous INTR request */
+ BX_PIC_THIS s.master_pic.auto_eoi = 0;
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0;
+ if (value & 0x02)
+ BX_PANIC(("master: ICW1: single mode not supported"));
+ if (value & 0x08) {
+ BX_PANIC(("master: ICW1: level sensitive mode not supported"));
+ }
+ else {
+ BX_DEBUG(("master: ICW1: edge triggered mode selected"));
+ }
+ BX_SET_INTR(0);
+ return;
+ }
+
+ if ( (value & 0x18) == 0x08 ) { /* OCW3 */
+ Bit8u special_mask, poll, read_op;
+
+ special_mask = (value & 0x60) >> 5;
+ poll = (value & 0x04) >> 2;
+ read_op = (value & 0x03);
+ if (poll) {
+ BX_PIC_THIS s.master_pic.polled = 1;
+ return;
+ }
+ if (read_op == 0x02) /* read IRR */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0;
+ else if (read_op == 0x03) /* read ISR */
+ BX_PIC_THIS s.master_pic.read_reg_select = 1;
+ if (special_mask == 0x02) { /* cancel special mask */
+ BX_PIC_THIS s.master_pic.special_mask = 0;
+ }
+ else if (special_mask == 0x03) { /* set specific mask */
+ BX_PIC_THIS s.master_pic.special_mask = 1;
+ service_master_pic();
+ }
+ return;
+ }
+
+ /* OCW2 */
+ switch (value) {
+ case 0x00: // Rotate in auto eoi mode clear
+ case 0x80: // Rotate in auto eoi mode set
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = (value != 0);
+ break;
+ case 0x0A: /* select read interrupt request register */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0;
+ break;
+ case 0x0B: /* select read interrupt in-service register */
+ BX_PIC_THIS s.master_pic.read_reg_select = 1;
+ break;
+
+ case 0xA0: // Rotate on non-specific end of interrupt
+ case 0x20: /* end of interrupt command */
+
+ clear_highest_interrupt(& BX_PIC_THIS s.master_pic);
+
+ if(value == 0xA0) {// Rotate in Auto-EOI mode
+ BX_PIC_THIS s.master_pic.lowest_priority ++;
+ if(BX_PIC_THIS s.master_pic.lowest_priority > 7)
+ BX_PIC_THIS s.master_pic.lowest_priority = 0;
+ }
+
+ service_master_pic();
+ break;
+
+ case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored
+ BX_INFO(("IRQ no-op"));
+ break;
+
+ case 0x60: /* specific EOI 0 */
+ case 0x61: /* specific EOI 1 */
+ case 0x62: /* specific EOI 2 */
+ case 0x63: /* specific EOI 3 */
+ case 0x64: /* specific EOI 4 */
+ case 0x65: /* specific EOI 5 */
+ case 0x66: /* specific EOI 6 */
+ case 0x67: /* specific EOI 7 */
+ BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0x60));
+ service_master_pic();
+ break;
+
+ // IRQ lowest priority commands
+ case 0xC0: // 0 7 6 5 4 3 2 1
+ case 0xC1: // 1 0 7 6 5 4 3 2
+ case 0xC2: // 2 1 0 7 6 5 4 3
+ case 0xC3: // 3 2 1 0 7 6 5 4
+ case 0xC4: // 4 3 2 1 0 7 6 5
+ case 0xC5: // 5 4 3 2 1 0 7 6
+ case 0xC6: // 6 5 4 3 2 1 0 7
+ case 0xC7: // 7 6 5 4 3 2 1 0
+ BX_INFO(("IRQ lowest command 0x%x", value));
+ BX_PIC_THIS s.master_pic.lowest_priority = value - 0xC0;
+ break;
+
+ case 0xE0: // specific EOI and rotate 0
+ case 0xE1: // specific EOI and rotate 1
+ case 0xE2: // specific EOI and rotate 2
+ case 0xE3: // specific EOI and rotate 3
+ case 0xE4: // specific EOI and rotate 4
+ case 0xE5: // specific EOI and rotate 5
+ case 0xE6: // specific EOI and rotate 6
+ case 0xE7: // specific EOI and rotate 7
+ BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0xE0));
+ BX_PIC_THIS s.master_pic.lowest_priority = (value - 0xE0);
+ service_master_pic();
+
+ break;
+
+ default:
+ BX_PANIC(("write to port 20h = %02x", value));
+ } /* switch (value) */
+ break;
+
+ case 0x21:
+ /* initialization mode operation */
+ if (BX_PIC_THIS s.master_pic.init.in_init) {
+ switch (BX_PIC_THIS s.master_pic.init.byte_expected) {
+ case 2:
+ BX_PIC_THIS s.master_pic.interrupt_offset = value & 0xf8;
+ BX_PIC_THIS s.master_pic.init.byte_expected = 3;
+ BX_DEBUG(("master: init command 2 = %02x", (unsigned) value));
+ BX_DEBUG((" offset = INT %02x",
+ BX_PIC_THIS s.master_pic.interrupt_offset));
+ return;
+ break;
+ case 3:
+ BX_DEBUG(("master: init command 3 = %02x", (unsigned) value));
+ if (BX_PIC_THIS s.master_pic.init.requires_4) {
+ BX_PIC_THIS s.master_pic.init.byte_expected = 4;
+ }
+ else {
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ }
+ return;
+ break;
+ case 4:
+ BX_DEBUG(("master: init command 4 = %02x", (unsigned) value));
+ if (value & 0x02) {
+ BX_DEBUG((" auto EOI"));
+ BX_PIC_THIS s.master_pic.auto_eoi = 1;
+ }
+ else {
+ BX_DEBUG(("normal EOI interrupt"));
+ BX_PIC_THIS s.master_pic.auto_eoi = 0;
+ }
+ if (value & 0x01) {
+ BX_DEBUG((" 80x86 mode"));
+ } else
+ BX_PANIC((" not 80x86 mode"));
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ return;
+ break;
+ default:
+ BX_PANIC(("master expecting bad init command"));
+ }
+ }
+
+ /* normal operation */
+ BX_DEBUG(("setting master pic IMR to %02x", value));
+ BX_PIC_THIS s.master_pic.imr = value;
+ service_master_pic();
+ return;
+ break;
+
+ case 0xA0:
+ if (value & 0x10) { /* initialization command 1 */
+ BX_DEBUG(("slave: init command 1 found"));
+ BX_DEBUG((" requires 4 = %u",
+ (unsigned) (value & 0x01) ));
+ BX_DEBUG((" cascade mode: [0=cascade,1=single] %u",
+ (unsigned) ((value & 0x02) >> 1)));
+ BX_PIC_THIS s.slave_pic.init.in_init = 1;
+ BX_PIC_THIS s.slave_pic.init.requires_4 = (value & 0x01);
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 2; /* operation command 2 */
+ BX_PIC_THIS s.slave_pic.imr = 0x00; /* clear irq mask */
+ BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.slave_pic.lowest_priority = 7;
+ BX_PIC_THIS s.slave_pic.INT = 0; /* reprogramming clears previous INTR request */
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0;
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0;
+ if (value & 0x02)
+ BX_PANIC(("slave: ICW1: single mode not supported"));
+ if (value & 0x08) {
+ BX_PANIC(("slave: ICW1: level sensitive mode not supported"));
+ }
+ else {
+ BX_DEBUG(("slave: ICW1: edge triggered mode selected"));
+ }
+ return;
+ }
+
+ if ( (value & 0x18) == 0x08 ) { /* OCW3 */
+ Bit8u special_mask, poll, read_op;
+
+ special_mask = (value & 0x60) >> 5;
+ poll = (value & 0x04) >> 2;
+ read_op = (value & 0x03);
+ if (poll) {
+ BX_PIC_THIS s.slave_pic.polled = 1;
+ return;
+ }
+ if (read_op == 0x02) /* read IRR */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0;
+ else if (read_op == 0x03) /* read ISR */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 1;
+ if (special_mask == 0x02) { /* cancel special mask */
+ BX_PIC_THIS s.slave_pic.special_mask = 0;
+ }
+ else if (special_mask == 0x03) { /* set specific mask */
+ BX_PIC_THIS s.slave_pic.special_mask = 1;
+ service_slave_pic();
+ }
+ return;
+ }
+
+ switch (value) {
+ case 0x00: // Rotate in auto eoi mode clear
+ case 0x80: // Rotate in auto eoi mode set
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = (value != 0);
+ break;
+
+ case 0x0A: /* select read interrupt request register */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0;
+ break;
+ case 0x0B: /* select read interrupt in-service register */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 1;
+ break;
+
+ case 0xA0: // Rotate on non-specific end of interrupt
+ case 0x20: /* end of interrupt command */
+
+ clear_highest_interrupt(& BX_PIC_THIS s.slave_pic);
+
+ if(value == 0xA0) {// Rotate in Auto-EOI mode
+ BX_PIC_THIS s.slave_pic.lowest_priority ++;
+ if(BX_PIC_THIS s.slave_pic.lowest_priority > 7)
+ BX_PIC_THIS s.slave_pic.lowest_priority = 0;
+ }
+
+ service_slave_pic();
+ break;
+
+ case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored
+ BX_INFO(("IRQ no-op"));
+ break;
+
+ case 0x60: /* specific EOI 0 */
+ case 0x61: /* specific EOI 1 */
+ case 0x62: /* specific EOI 2 */
+ case 0x63: /* specific EOI 3 */
+ case 0x64: /* specific EOI 4 */
+ case 0x65: /* specific EOI 5 */
+ case 0x66: /* specific EOI 6 */
+ case 0x67: /* specific EOI 7 */
+ BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0x60));
+ service_slave_pic();
+ break;
+
+ // IRQ lowest priority commands
+ case 0xC0: // 0 7 6 5 4 3 2 1
+ case 0xC1: // 1 0 7 6 5 4 3 2
+ case 0xC2: // 2 1 0 7 6 5 4 3
+ case 0xC3: // 3 2 1 0 7 6 5 4
+ case 0xC4: // 4 3 2 1 0 7 6 5
+ case 0xC5: // 5 4 3 2 1 0 7 6
+ case 0xC6: // 6 5 4 3 2 1 0 7
+ case 0xC7: // 7 6 5 4 3 2 1 0
+ BX_INFO(("IRQ lowest command 0x%x", value));
+ BX_PIC_THIS s.slave_pic.lowest_priority = value - 0xC0;
+ break;
+
+ case 0xE0: // specific EOI and rotate 0
+ case 0xE1: // specific EOI and rotate 1
+ case 0xE2: // specific EOI and rotate 2
+ case 0xE3: // specific EOI and rotate 3
+ case 0xE4: // specific EOI and rotate 4
+ case 0xE5: // specific EOI and rotate 5
+ case 0xE6: // specific EOI and rotate 6
+ case 0xE7: // specific EOI and rotate 7
+ BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0xE0));
+ BX_PIC_THIS s.slave_pic.lowest_priority = (value - 0xE0);
+ service_slave_pic();
+
+ break;
+
+ default:
+ BX_PANIC(("write to port A0h = %02x", value));
+ } /* switch (value) */
+ break;
+
+ case 0xA1:
+ /* initialization mode operation */
+ if (BX_PIC_THIS s.slave_pic.init.in_init) {
+ switch (BX_PIC_THIS s.slave_pic.init.byte_expected) {
+ case 2:
+ BX_PIC_THIS s.slave_pic.interrupt_offset = value & 0xf8;
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 3;
+ BX_DEBUG(("slave: init command 2 = %02x", (unsigned) value));
+ BX_DEBUG((" offset = INT %02x",
+ BX_PIC_THIS s.slave_pic.interrupt_offset));
+ return;
+ break;
+ case 3:
+ BX_DEBUG(("slave: init command 3 = %02x", (unsigned) value));
+ if (BX_PIC_THIS s.slave_pic.init.requires_4) {
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 4;
+ } else {
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ }
+ return;
+ break;
+ case 4:
+ BX_DEBUG(("slave: init command 4 = %02x", (unsigned) value));
+ if (value & 0x02) {
+ BX_DEBUG((" auto EOI"));
+ BX_PIC_THIS s.slave_pic.auto_eoi = 1;
+ }
+ else {
+ BX_DEBUG(("normal EOI interrupt"));
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0;
+ }
+ if (value & 0x01) {
+ BX_DEBUG((" 80x86 mode"));
+ } else
+ BX_PANIC((" not 80x86 mode"));
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ return;
+ break;
+ default:
+ BX_PANIC(("slave: expecting bad init command"));
+ }
+ }
+
+ /* normal operation */
+ BX_DEBUG(("setting slave pic IMR to %02x", value));
+ BX_PIC_THIS s.slave_pic.imr = value;
+ service_slave_pic();
+ return;
+ break;
+ } /* switch (address) */
+
+ return;
+}
+
+// new IRQ signal handling routines
+
+ void
+bx_pic_c::lower_irq(unsigned irq_no)
+{
+#if BX_SUPPORT_APIC
+ // forward this function call to the ioapic too
+ if (DEV_ioapic_present())
+ bx_devices.ioapic->untrigger_irq (irq_no, -1);
+#endif
+
+ if ((irq_no <= 7) && (BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) {
+ BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no));
+ BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 0;
+ BX_PIC_THIS s.master_pic.irr &= ~(1 << irq_no);
+ if ((BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr) == 0) {
+ BX_SET_INTR(0);
+ BX_PIC_THIS s.master_pic.INT = 0;
+ }
+ } else if ((irq_no > 7) && (irq_no <= 15) &&
+ (BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) {
+ BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no));
+ BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 0;
+ BX_PIC_THIS s.slave_pic.irr &= ~(1 << (irq_no - 8));
+ if ((BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr) == 0) {
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ lower_irq(2);
+ }
+ }
+}
+
+ void
+bx_pic_c::raise_irq(unsigned irq_no)
+{
+#if BX_SUPPORT_APIC
+ // forward this function call to the ioapic too
+ bx_devices.ioapic->trigger_irq (irq_no, -1);
+#endif
+
+ if ((irq_no <= 7) && (!BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) {
+ BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no));
+ BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 1;
+ BX_PIC_THIS s.master_pic.irr |= (1 << irq_no);
+ service_master_pic();
+ } else if ((irq_no > 7) && (irq_no <= 15) &&
+ (!BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) {
+ BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no));
+ BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 1;
+ BX_PIC_THIS s.slave_pic.irr |= (1 << (irq_no - 8));
+ service_slave_pic();
+ }
+}
+
+void bx_pic_c::clear_highest_interrupt(bx_pic_t *pic)
+{
+ int irq;
+ int lowest_priority;
+ int highest_priority;
+
+ /* clear highest current in service bit */
+ lowest_priority = pic->lowest_priority;
+ highest_priority = lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ irq = highest_priority;
+ do {
+ if (pic->isr & (1 << irq)) {
+ pic->isr &= ~(1 << irq);
+ break; /* Return mask of bit cleared. */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != highest_priority);
+
+}
+
+ /* */
+ void
+bx_pic_c::service_master_pic(void)
+{
+ Bit8u unmasked_requests;
+ int irq;
+ Bit8u isr, max_irq;
+ Bit8u highest_priority = BX_PIC_THIS s.master_pic.lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ if (BX_PIC_THIS s.master_pic.INT) { /* last interrupt still not acknowleged */
+ return;
+ }
+
+ if (BX_PIC_THIS s.master_pic.special_mask) {
+ /* all priorities may be enabled. check all IRR bits except ones
+ * which have corresponding ISR bits set
+ */
+ max_irq = highest_priority;
+ }
+ else { /* normal mode */
+ /* Find the highest priority IRQ that is enabled due to current ISR */
+ isr = BX_PIC_THIS s.master_pic.isr;
+ if (isr) {
+ max_irq = highest_priority;
+ while ( (isr & (1 << max_irq)) == 0) {
+ max_irq++;
+ if(max_irq > 7)
+ max_irq = 0;
+ }
+ if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service,
+ * no other priorities allowed */
+ if (max_irq > 7) BX_PANIC(("error in service_master_pic()"));
+ }
+ else
+ max_irq = highest_priority; /* 0..7 bits in ISR are cleared */
+ }
+
+
+ /* now, see if there are any higher priority requests */
+ if ((unmasked_requests = (BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr)) ) {
+ irq = highest_priority;
+ do {
+ /* for special mode, since we're looking at all IRQ's, skip if
+ * current IRQ is already in-service
+ */
+ if ( ! (BX_PIC_THIS s.master_pic.special_mask && ((BX_PIC_THIS s.master_pic.isr >> irq) & 0x01)) ) {
+ if (unmasked_requests & (1 << irq)) {
+ BX_DEBUG(("signalling IRQ(%u)", (unsigned) irq));
+ BX_PIC_THIS s.master_pic.INT = 1;
+ BX_SET_INTR(1);
+ BX_PIC_THIS s.master_pic.irq = irq;
+ return;
+ } /* if (unmasked_requests & ... */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != max_irq); /* do ... */
+ } /* if (unmasked_requests = ... */
+}
+
+
+ void
+bx_pic_c::service_slave_pic(void)
+{
+ Bit8u unmasked_requests;
+ int irq;
+ Bit8u isr, max_irq;
+ Bit8u highest_priority = BX_PIC_THIS s.slave_pic.lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ if (BX_PIC_THIS s.slave_pic.INT) { /* last interrupt still not acknowleged */
+ return;
+ }
+
+ if (BX_PIC_THIS s.slave_pic.special_mask) {
+ /* all priorities may be enabled. check all IRR bits except ones
+ * which have corresponding ISR bits set
+ */
+ max_irq = highest_priority;
+ }
+ else { /* normal mode */
+ /* Find the highest priority IRQ that is enabled due to current ISR */
+ isr = BX_PIC_THIS s.slave_pic.isr;
+ if (isr) {
+ max_irq = highest_priority;
+ while ( (isr & (1 << max_irq)) == 0) {
+ max_irq++;
+ if(max_irq > 7)
+ max_irq = 0;
+ }
+ if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service,
+ * no other priorities allowed */
+ if (max_irq > 7) BX_PANIC(("error in service_master_pic()"));
+ }
+ else
+ max_irq = highest_priority; /* 0..7 bits in ISR are cleared */
+ }
+
+
+ /* now, see if there are any higher priority requests */
+ if ((unmasked_requests = (BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr)) ) {
+ irq = highest_priority;
+ do {
+ /* for special mode, since we're looking at all IRQ's, skip if
+ * current IRQ is already in-service
+ */
+ if ( ! (BX_PIC_THIS s.slave_pic.special_mask && ((BX_PIC_THIS s.slave_pic.isr >> irq) & 0x01)) ) {
+ if (unmasked_requests & (1 << irq)) {
+ BX_DEBUG(("slave: signalling IRQ(%u)", (unsigned) 8 + irq));
+
+ BX_PIC_THIS s.slave_pic.INT = 1;
+ BX_PIC_THIS s.slave_pic.irq = irq;
+ BX_PIC_THIS raise_irq(2); /* request IRQ 2 on master pic */
+ return;
+ } /* if (unmasked_requests & ... */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != max_irq); /* do ... */
+ } /* if (unmasked_requests = ... */
+}
+
+
+ /* CPU handshakes with PIC after acknowledging interrupt */
+ Bit8u
+bx_pic_c::IAC(void)
+{
+ Bit8u vector;
+ Bit8u irq;
+
+ BX_SET_INTR(0);
+ BX_PIC_THIS s.master_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.irr &= ~(1 << BX_PIC_THIS s.master_pic.irq);
+ // In autoeoi mode don't set the isr bit.
+ if(!BX_PIC_THIS s.master_pic.auto_eoi)
+ BX_PIC_THIS s.master_pic.isr |= (1 << BX_PIC_THIS s.master_pic.irq);
+ else if(BX_PIC_THIS s.master_pic.rotate_on_autoeoi)
+ BX_PIC_THIS s.master_pic.lowest_priority = BX_PIC_THIS s.master_pic.irq;
+
+ if (BX_PIC_THIS s.master_pic.irq != 2) {
+ irq = BX_PIC_THIS s.master_pic.irq;
+ vector = irq + BX_PIC_THIS s.master_pic.interrupt_offset;
+ }
+ else { /* IRQ2 = slave pic IRQ8..15 */
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.IRQ_line[2] = 0;
+ irq = BX_PIC_THIS s.slave_pic.irq;
+ vector = irq + BX_PIC_THIS s.slave_pic.interrupt_offset;
+ BX_PIC_THIS s.slave_pic.irr &= ~(1 << BX_PIC_THIS s.slave_pic.irq);
+ // In autoeoi mode don't set the isr bit.
+ if(!BX_PIC_THIS s.slave_pic.auto_eoi)
+ BX_PIC_THIS s.slave_pic.isr |= (1 << BX_PIC_THIS s.slave_pic.irq);
+ else if(BX_PIC_THIS s.slave_pic.rotate_on_autoeoi)
+ BX_PIC_THIS s.slave_pic.lowest_priority = BX_PIC_THIS s.slave_pic.irq;
+ service_slave_pic();
+ irq += 8; // for debug printing purposes
+ }
+
+ service_master_pic();
+
+ BX_DBG_IAC_REPORT(vector, irq);
+ return(vector);
+}
+
+ void
+bx_pic_c::show_pic_state(void)
+{
+#if defined(BX_DEBUGGER) && (BX_DEBUGGER == 1)
+dbg_printf("s.master_pic.imr = %02x\n", BX_PIC_THIS s.master_pic.imr);
+dbg_printf("s.master_pic.isr = %02x\n", BX_PIC_THIS s.master_pic.isr);
+dbg_printf("s.master_pic.irr = %02x\n", BX_PIC_THIS s.master_pic.irr);
+dbg_printf("s.master_pic.irq = %02x\n", BX_PIC_THIS s.master_pic.irq);
+dbg_printf("s.slave_pic.imr = %02x\n", BX_PIC_THIS s.slave_pic.imr);
+dbg_printf("s.slave_pic.isr = %02x\n", BX_PIC_THIS s.slave_pic.isr);
+dbg_printf("s.slave_pic.irr = %02x\n", BX_PIC_THIS s.slave_pic.irr);
+dbg_printf("s.slave_pic.irq = %02x\n", BX_PIC_THIS s.slave_pic.irq);
+#endif
+}