Browse Source

pic: state, fix acknowledge_irq not considering irq mask

Fabian 4 months ago
parent
commit
8b51ae6c78
3 changed files with 133 additions and 613 deletions
  1. 83 2
      src/cpu.js
  2. 0 574
      src/pic.js
  3. 50 37
      src/rust/cpu/pic.rs

+ 83 - 2
src/cpu.js

@@ -278,6 +278,9 @@ CPU.prototype.wasm_patch = function()
     this.svga_fill_pixel_buffer = get_import("svga_fill_pixel_buffer");
     this.svga_mark_dirty = get_import("svga_mark_dirty");
 
+    this.get_pic_addr_master = get_import("get_pic_addr_master");
+    this.get_pic_addr_slave = get_import("get_pic_addr_slave");
+
     this.zstd_create_ctx = get_import("zstd_create_ctx");
     this.zstd_get_src_ptr = get_import("zstd_get_src_ptr");
     this.zstd_free_ctx = get_import("zstd_free_ctx");
@@ -386,7 +389,7 @@ CPU.prototype.get_state = function()
     state[57] = this.devices.hda;
     state[58] = this.devices.pit;
     state[59] = this.devices.net;
-    //state[60] = this.devices.pic;
+    state[60] = this.get_state_pic();
     state[61] = this.devices.sb16;
 
     state[62] = this.fw_value;
@@ -418,6 +421,46 @@ CPU.prototype.get_state = function()
     return state;
 };
 
+CPU.prototype.get_state_pic = function()
+{
+    const pic_size = 13;
+    const pic = new Uint8Array(this.wasm_memory.buffer, this.get_pic_addr_master(), pic_size);
+    const pic_slave = new Uint8Array(this.wasm_memory.buffer, this.get_pic_addr_slave(), pic_size);
+
+    const state = [];
+    const state_slave = [];
+
+    state[0] = pic[0]; // irq_mask
+    state[1] = pic[1]; // irq_map
+    state[2] = pic[2]; // isr
+    state[3] = pic[3]; // irr
+    state[4] = pic[4]; // is_master
+    state[5] = state_slave;
+    state[6] = pic[6]; // expect_icw4
+    state[7] = pic[7]; // state
+    state[8] = pic[8]; // read_isr
+    state[9] = pic[9]; // auto_eoi
+    state[10] = pic[10]; // special_mask_mode
+    state[11] = pic[11]; // elcr
+    state[12] = pic[12]; // irq_value (undefined in old state images)
+
+    state_slave[0] = pic_slave[0]; // irq_mask
+    state_slave[1] = pic_slave[1]; // irq_map
+    state_slave[2] = pic_slave[2]; // isr
+    state_slave[3] = pic_slave[3]; // irr
+    state_slave[4] = pic_slave[4]; // is_master
+    state_slave[5] = null;
+    state_slave[6] = pic_slave[6]; // expect_icw4
+    state_slave[7] = pic_slave[7]; // state
+    state_slave[8] = pic_slave[8]; // read_isr
+    state_slave[9] = pic_slave[9]; // auto_eoi
+    state_slave[10] = pic_slave[10]; // elcr
+    state_slave[12] = pic_slave[12]; // irq_value (undefined in old state images)
+    state_slave[12] = pic_slave[12]; // special_mask_mode (undefined in old state images)
+
+    return state;
+};
+
 CPU.prototype.set_state = function(state)
 {
     this.memory_size[0] = state[0];
@@ -482,7 +525,7 @@ CPU.prototype.set_state = function(state)
     this.devices.hda && this.devices.hda.set_state(state[57]);
     this.devices.pit && this.devices.pit.set_state(state[58]);
     this.devices.net && this.devices.net.set_state(state[59]);
-    //this.devices.pic && this.devices.pic.set_state(state[60]);
+    this.set_state_pic(state[60]);
     this.devices.sb16 && this.devices.sb16.set_state(state[61]);
 
     this.devices.uart1 && this.devices.uart1.set_state(state[79]);
@@ -518,6 +561,44 @@ CPU.prototype.set_state = function(state)
     this.jit_clear_cache();
 };
 
+CPU.prototype.set_state_pic = function(state)
+{
+    // Note: This could exists for compatibility with old state images
+    // It should be deleted when the state version changes
+
+    const pic_size = 13;
+    const pic = new Uint8Array(this.wasm_memory.buffer, this.get_pic_addr_master(), pic_size);
+    const pic_slave = new Uint8Array(this.wasm_memory.buffer, this.get_pic_addr_slave(), pic_size);
+
+    pic[0] = state[0]; // irq_mask
+    pic[1] = state[1]; // irq_map
+    pic[2] = state[2]; // isr
+    pic[3] = state[3]; // irr
+    pic[4] = state[4]; // is_master
+    const state_slave = state[5];
+    pic[6] = state[6]; // expect_icw4
+    pic[7] = state[7]; // state
+    pic[8] = state[8]; // read_isr
+    pic[9] = state[9]; // auto_eoi
+    pic[10] = state[10]; // special_mask_mode
+    pic[11] = state[11]; // elcr
+    pic[12] = state[12]; // irq_value (undefined in old state images)
+
+    pic_slave[0] = state_slave[0]; // irq_mask
+    pic_slave[1] = state_slave[1]; // irq_map
+    pic_slave[2] = state_slave[2]; // isr
+    pic_slave[3] = state_slave[3]; // irr
+    pic_slave[4] = state_slave[4]; // is_master
+    // dummy
+    pic_slave[6] = state_slave[6]; // expect_icw4
+    pic_slave[7] = state_slave[7]; // state
+    pic_slave[8] = state_slave[8]; // read_isr
+    pic_slave[9] = state_slave[9]; // auto_eoi
+    pic_slave[10] = state_slave[10]; // elcr
+    pic_slave[12] = state_slave[12]; // irq_value (undefined in old state images)
+    pic_slave[12] = state_slave[12]; // special_mask_mode (undefined in old state images)
+};
+
 CPU.prototype.pack_memory = function()
 {
     dbg_assert((this.mem8.length & 0xFFF) === 0);

+ 0 - 574
src/pic.js

@@ -1,574 +0,0 @@
-"use strict";
-
-/** @const */
-var PIC_LOG_VERBOSE = false;
-
-/**
- * Programmable Interrupt Controller
- * http://stanislavs.org/helppc/8259.html
- *
- * @constructor
- * @param {CPU} cpu
- * @param {PIC=} master
- */
-function PIC(cpu, master)
-{
-    /**
-     * all irqs off
-     * @type {number}
-     */
-    this.irq_mask = 0;
-
-    /**
-     * @type {number}
-     *
-     * Bogus default value (both master and slave mapped to 0).
-     * Will be initialized by the BIOS
-     */
-    this.irq_map = 0;
-
-    /**
-     * in-service register
-     * Holds interrupts that are currently being serviced
-     * @type {number}
-     */
-    this.isr = 0;
-
-    /**
-     * interrupt request register
-     * Holds interrupts that have been requested
-     * @type {number}
-     */
-    this.irr = 0;
-
-    this.irq_value = 0;
-
-    /**
-     * @type {number}
-     */
-    this.requested_irq = -1;
-
-    this.master = master;
-    this.is_master = this.master === undefined;
-    this.slave = undefined;
-
-    this.name = this.is_master ? "master" : "slave ";
-
-    this.expect_icw4 = false;
-    this.state = 0;
-    this.read_isr = 0;
-    this.auto_eoi = 1;
-    this.special_mask_mode = 0;
-
-    this.elcr = 0;
-
-    this.cpu = cpu;
-
-    // Checking for callable interrupts:
-    // (cpu changes interrupt flag) -> cpu.handle_irqs -> pic.check_irqs -> cpu.pic_call_irq
-    // (pic changes isr/irr) -> cpu.handle_irqs -> ...
-
-    // triggering irqs:
-    // (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> cpu.handle_irqs -> (see above)
-
-
-    if(this.is_master)
-    {
-        this.slave = new PIC(this.cpu, this);
-
-        this.check_irqs = function()
-        {
-            if(this.requested_irq >= 0)
-            {
-                PIC_LOG_VERBOSE && dbg_log("master> Already requested irq: " + this.requested_irq, LOG_PIC);
-                this.cpu.handle_irqs();
-                return;
-            }
-
-            var enabled_irr = this.irr & this.irq_mask;
-
-            if(!enabled_irr)
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("master> no unmasked irrs. irr=" + h(this.irr, 2) +
-                            " mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
-                }
-                return;
-            }
-
-            var irq_mask = enabled_irr & -enabled_irr;
-            var special_mask = this.special_mask_mode ? this.irq_mask : -1;
-
-            if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
-            {
-                // wait for eoi of higher or same priority interrupt
-                dbg_log("master> higher prio: isr=" + h(this.isr, 2) +
-                        " mask=" + h(this.irq_mask & 0xff, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
-                return;
-            }
-
-            dbg_assert(irq_mask !== 0);
-            var irq_number = v86util.int_log2_byte(irq_mask);
-            dbg_assert(irq_mask === (1 << irq_number));
-
-            PIC_LOG_VERBOSE && dbg_log("master> request irq " + irq_number, LOG_PIC);
-
-            this.requested_irq = irq_number;
-            this.cpu.handle_irqs();
-        };
-
-        this.acknowledge_irq = function()
-        {
-            if(this.requested_irq === -1)
-            {
-                return;
-            }
-
-            if(this.irr === 0)
-            {
-                PIC_LOG_VERBOSE && dbg_log("master> spurious requested=" + this.requested_irq, LOG_PIC);
-                this.requested_irq = -1;
-                //this.cpu.pic_call_irq(this.irq_map | 7);
-                return;
-            }
-            dbg_assert(this.irr); // spurious
-            dbg_assert(this.requested_irq >= 0);
-
-            var irq_mask = 1 << this.requested_irq;
-
-            if((this.elcr & irq_mask) === 0) // not in level mode
-            {
-                this.irr &= ~irq_mask;
-            }
-
-            if(!this.auto_eoi)
-            {
-                this.isr |= irq_mask;
-            }
-
-            PIC_LOG_VERBOSE && dbg_log("master> acknowledge " + this.requested_irq, LOG_PIC);
-            if(this.requested_irq === 2)
-            {
-                this.slave.acknowledge_irq();
-            }
-            else
-            {
-                this.cpu.pic_call_irq(this.irq_map | this.requested_irq);
-            }
-
-            this.requested_irq = -1;
-            this.check_irqs();
-        };
-    }
-    else
-    {
-        // is slave
-        this.check_irqs = function()
-        {
-            if(this.requested_irq >= 0)
-            {
-                PIC_LOG_VERBOSE && dbg_log("slave > Already requested irq: " + this.requested_irq, LOG_PIC);
-                this.cpu.handle_irqs();
-                return;
-            }
-
-            var enabled_irr = this.irr & this.irq_mask;
-
-            if(!enabled_irr)
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("slave > no unmasked irrs. irr=" + h(this.irr, 2) +
-                            " mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
-                }
-                return;
-            }
-
-            var irq_mask = enabled_irr & -enabled_irr;
-            var special_mask = this.special_mask_mode ? this.irq_mask : -1;
-
-            if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
-            {
-                // wait for eoi of higher or same priority interrupt
-                PIC_LOG_VERBOSE && dbg_log("slave > higher prio: isr=" + h(this.isr, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
-                return;
-            }
-
-            dbg_assert(irq_mask !== 0);
-            var irq_number = v86util.int_log2_byte(irq_mask);
-            dbg_assert(irq_mask === (1 << irq_number));
-
-            PIC_LOG_VERBOSE && dbg_log("slave > request irq " + irq_number, LOG_PIC);
-            this.requested_irq = irq_number;
-            this.master.set_irq(2);
-        };
-
-        this.acknowledge_irq = function()
-        {
-            if(this.requested_irq === -1)
-            {
-                return;
-            }
-
-            if(this.irr === 0)
-            {
-                PIC_LOG_VERBOSE && dbg_log("slave > spurious requested=" + this.requested_irq, LOG_PIC);
-                this.requested_irq = -1;
-                this.master.irq_value &= ~(1 << 2);
-                this.cpu.pic_call_irq(this.irq_map | 7);
-                return;
-            }
-
-            dbg_assert(this.irr); // spurious
-            dbg_assert(this.requested_irq >= 0);
-
-            var irq_mask = 1 << this.requested_irq;
-
-            if((this.elcr & irq_mask) === 0) // not in level mode
-            {
-                this.irr &= ~irq_mask;
-            }
-
-            if(!this.auto_eoi)
-            {
-                this.isr |= irq_mask;
-            }
-
-            this.master.irq_value &= ~(1 << 2);
-            PIC_LOG_VERBOSE && dbg_log("slave > acknowledge " + this.requested_irq, LOG_PIC);
-            this.cpu.pic_call_irq(this.irq_map | this.requested_irq);
-
-            this.requested_irq = -1;
-            this.check_irqs();
-        };
-    }
-
-    this.dump = function()
-    {
-        dbg_log("mask: " + h(this.irq_mask & 0xFF), LOG_PIC);
-        dbg_log("base: " + h(this.irq_map), LOG_PIC);
-        dbg_log("requested: " + h(this.irr), LOG_PIC);
-        dbg_log("serviced: " + h(this.isr), LOG_PIC);
-
-        if(this.is_master)
-        {
-            this.slave.dump();
-        }
-    };
-
-    var io_base;
-    var iobase_high;
-    if(this.is_master)
-    {
-        io_base = 0x20;
-        iobase_high = 0x4D0;
-    }
-    else
-    {
-        io_base = 0xA0;
-        iobase_high = 0x4D1;
-    }
-
-    this.cpu.io.register_write(io_base, this, this.port20_write);
-    this.cpu.io.register_read(io_base, this, this.port20_read);
-
-    this.cpu.io.register_write(io_base | 1, this, this.port21_write);
-    this.cpu.io.register_read(io_base | 1, this, this.port21_read);
-
-    this.cpu.io.register_write(iobase_high, this, this.port4D0_write);
-    this.cpu.io.register_read(iobase_high, this, this.port4D0_read);
-
-
-    if(this.is_master)
-    {
-        this.set_irq = function(irq_number)
-        {
-            dbg_assert(irq_number >= 0 && irq_number < 16);
-
-            if(irq_number >= 8)
-            {
-                this.slave.set_irq(irq_number - 8);
-                return;
-            }
-
-            var irq_mask = 1 << irq_number;
-            if((this.irq_value & irq_mask) === 0)
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("master> set irq " + irq_number, LOG_PIC);
-                }
-
-                this.irr |= irq_mask;
-                this.irq_value |= irq_mask;
-                this.check_irqs();
-            }
-            else
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("master> set irq " + irq_number + ": already set!", LOG_PIC);
-                }
-            }
-        };
-
-        this.clear_irq = function(irq_number)
-        {
-            dbg_assert(irq_number >= 0 && irq_number < 16);
-            if(PIC_LOG_VERBOSE)
-            {
-                dbg_log("master> clear irq " + irq_number, LOG_PIC);
-            }
-
-            if(irq_number >= 8)
-            {
-                this.slave.clear_irq(irq_number - 8);
-                return;
-            }
-
-            var irq_mask = 1 << irq_number;
-            if(this.irq_value & irq_mask)
-            {
-                this.irq_value &= ~irq_mask;
-                this.irr &= ~irq_mask;
-                this.check_irqs();
-            }
-        };
-    }
-    else
-    {
-        this.set_irq = function(irq_number)
-        {
-            dbg_assert(irq_number >= 0 && irq_number < 8);
-
-            var irq_mask = 1 << irq_number;
-            if((this.irq_value & irq_mask) === 0)
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("slave > set irq " + irq_number, LOG_PIC);
-                }
-
-                this.irr |= irq_mask;
-                this.irq_value |= irq_mask;
-                this.check_irqs();
-            }
-            else
-            {
-                if(PIC_LOG_VERBOSE)
-                {
-                    dbg_log("slave > set irq " + irq_number + ": already set!", LOG_PIC);
-                }
-            }
-        };
-
-        this.clear_irq = function(irq_number)
-        {
-            dbg_assert(irq_number >= 0 && irq_number < 8);
-            if(PIC_LOG_VERBOSE)
-            {
-                dbg_log("slave > clear irq " + irq_number, LOG_PIC);
-            }
-
-            var irq_mask = 1 << irq_number;
-            if(this.irq_value & irq_mask)
-            {
-                this.irq_value &= ~irq_mask;
-                this.irr &= ~irq_mask;
-                this.check_irqs();
-            }
-        };
-    }
-
-    this.get_isr = function()
-    {
-        return this.isr;
-    };
-}
-
-PIC.prototype.get_state = function()
-{
-    var state = [];
-
-    state[0] = this.irq_mask;
-    state[1] = this.irq_map;
-    state[2] = this.isr;
-    state[3] = this.irr;
-    state[4] = this.is_master;
-    state[5] = this.slave;
-    state[6] = this.expect_icw4;
-    state[7] = this.state;
-    state[8] = this.read_isr;
-    state[9] = this.auto_eoi;
-    state[10] = this.elcr;
-
-    return state;
-};
-
-PIC.prototype.set_state = function(state)
-{
-    this.irq_mask = state[0];
-    this.irq_map = state[1];
-    this.isr = state[2];
-    this.irr = state[3];
-    this.is_master = state[4];
-    this.slave && this.slave.set_state(state[5]);
-    this.expect_icw4 = state[6];
-    this.state = state[7];
-    this.read_isr = state[8];
-    this.auto_eoi = state[9];
-    this.elcr = state[10];
-};
-
-PIC.prototype.port20_write = function(data_byte)
-{
-    //dbg_log("20 write: " + h(data_byte), LOG_PIC);
-    if(data_byte & 0x10) // xxxx1xxx
-    {
-        // icw1
-        dbg_log("icw1 = " + h(data_byte), LOG_PIC);
-        this.isr = 0;
-        this.irr = 0;
-        this.irq_mask = 0;
-        this.irq_value = 0;
-        this.auto_eoi = 1;
-        this.requested_irq = -1;
-
-        this.expect_icw4 = data_byte & 1;
-        this.state = 1;
-    }
-    else if(data_byte & 8) // xxx01xxx
-    {
-        // ocw3
-        dbg_log("ocw3: " + h(data_byte), LOG_PIC);
-        if(data_byte & 2)
-        {
-            this.read_isr = data_byte & 1;
-        }
-        if(data_byte & 4)
-        {
-            dbg_assert(false, "unimplemented: polling", LOG_PIC);
-        }
-        if(data_byte & 0x40)
-        {
-            this.special_mask_mode = (data_byte & 0x20) === 0x20;
-            dbg_log("special mask mode: " + this.special_mask_mode, LOG_PIC);
-        }
-    }
-    else // xxx00xxx
-    {
-        // ocw2
-        // end of interrupt
-        dbg_log("eoi: " + h(data_byte) + " (" + this.name + ")", LOG_PIC);
-
-        var eoi_type = data_byte >> 5;
-
-        if(eoi_type === 1)
-        {
-            // non-specific eoi
-            this.isr &= this.isr - 1;
-            dbg_log("new isr: " + h(this.isr, 2), LOG_PIC);
-        }
-        else if(eoi_type === 3)
-        {
-            // specific eoi
-            this.isr &= ~(1 << (data_byte & 7));
-        }
-        else if((data_byte & 0xC8) === 0xC0)
-        {
-            // os2 v4
-            let priority = data_byte & 7;
-            dbg_log("lowest priority: " + h(priority), LOG_PIC);
-        }
-        else
-        {
-            dbg_log("Unknown eoi: " + h(data_byte), LOG_PIC);
-            dbg_assert(false);
-            this.isr &= this.isr - 1;
-        }
-
-        this.check_irqs();
-    }
-};
-
-PIC.prototype.port20_read = function()
-{
-    if(this.read_isr)
-    {
-        dbg_log("read port 20h (isr): " + h(this.isr), LOG_PIC);
-        return this.isr;
-    }
-    else
-    {
-        dbg_log("read port 20h (irr): " + h(this.irr), LOG_PIC);
-        return this.irr;
-    }
-};
-
-PIC.prototype.port21_write = function(data_byte)
-{
-    //dbg_log("21 write: " + h(data_byte), LOG_PIC);
-    if(this.state === 0)
-    {
-        if(this.expect_icw4)
-        {
-            // icw4
-            this.expect_icw4 = false;
-            this.auto_eoi = data_byte & 2;
-            dbg_log("icw4: " + h(data_byte) + " autoeoi=" + this.auto_eoi, LOG_PIC);
-
-            if((data_byte & 1) === 0)
-            {
-                dbg_assert(false, "unimplemented: not 8086 mode", LOG_PIC);
-            }
-        }
-        else
-        {
-            // ocw1
-            this.irq_mask = ~data_byte;
-
-            if(PIC_LOG_VERBOSE)
-            {
-                dbg_log("interrupt mask: " + (this.irq_mask & 0xFF).toString(2) +
-                        " (" + this.name + ")", LOG_PIC);
-            }
-
-            this.check_irqs();
-        }
-    }
-    else if(this.state === 1)
-    {
-        // icw2
-        this.irq_map = data_byte;
-        dbg_log("interrupts are mapped to " + h(this.irq_map) +
-                " (" + this.name + ")", LOG_PIC);
-        this.state++;
-    }
-    else if(this.state === 2)
-    {
-        // icw3
-        this.state = 0;
-        dbg_log("icw3: " + h(data_byte), LOG_PIC);
-    }
-};
-
-PIC.prototype.port21_read = function()
-{
-    dbg_log("21h read " + h(~this.irq_mask & 0xff), LOG_PIC);
-    return ~this.irq_mask & 0xFF;
-};
-
-PIC.prototype.port4D0_read = function()
-{
-    dbg_log("elcr read: " + h(this.elcr, 2), LOG_PIC);
-    return this.elcr;
-};
-
-PIC.prototype.port4D0_write = function(value)
-{
-    dbg_log("elcr write: " + h(value, 2), LOG_PIC);
-    // set by seabios to 00 0C (only set for pci interrupts)
-    this.elcr = value;
-};
-

+ 50 - 37
src/rust/cpu/pic.rs

@@ -1,9 +1,15 @@
 #![allow(non_snake_case)]
 
+// Programmable Interrupt Controller
+// http://stanislavs.org/helppc/8259.html
+
 pub const PIC_LOG: bool = false;
 pub const PIC_LOG_VERBOSE: bool = false;
 use cpu::cpu;
 
+// Note: This layout is deliberately chosen to match the old JavaScript pic state
+// (cpu.get_state_pic depens on this layout)
+#[repr(C, packed)]
 struct Pic {
     irq_mask: u8,
 
@@ -17,19 +23,18 @@ struct Pic {
     // Holds interrupts that have been requested
     irr: u8,
 
-    irq_value: u8,
-
-    requested_irq: Option<u8>,
+    master: bool,
+    dummy: u8, // remove when state image is updated
 
     expect_icw4: bool,
     state: u8,
     read_isr: bool,
     auto_eoi: bool,
-    special_mask_mode: bool,
 
     elcr: u8,
 
-    master: bool,
+    irq_value: u8,
+    special_mask_mode: bool,
 }
 
 #[allow(non_upper_case_globals)]
@@ -46,7 +51,6 @@ static mut master: Pic = Pic {
     // Holds interrupts that have been requested
     irr: 0,
     irq_value: 0,
-    requested_irq: None,
     expect_icw4: false,
     state: 0,
     read_isr: false,
@@ -54,6 +58,7 @@ static mut master: Pic = Pic {
     special_mask_mode: false,
     elcr: 0,
     master: true,
+    dummy: 0,
 };
 
 #[allow(non_upper_case_globals)]
@@ -70,7 +75,6 @@ static mut slave: Pic = Pic {
     // Holds interrupts that have been requested
     irr: 0,
     irq_value: 0,
-    requested_irq: None,
     expect_icw4: false,
     state: 0,
     read_isr: false,
@@ -78,26 +82,27 @@ static mut slave: Pic = Pic {
     special_mask_mode: false,
     elcr: 0,
     master: false,
+    dummy: 0,
 };
 
 
 // Checking for callable interrupts:
-// (cpu changes interrupt flag) -> cpu.handle_irqs -> pic.check_irqs -> cpu.pic_call_irq
-// (pic changes isr/irr) -> cpu.handle_irqs -> ...
+// (cpu changes interrupt flag) -> cpu.handle_irqs -> pic_acknowledge_irq -> cpu.pic_call_irq
+// (pic changes isr/irr) -> pic.check_irqs -> cpu.handle_irqs -> ...
 
 // triggering irqs:
-// (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> cpu.handle_irqs -> (see above)
+// (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> pic.check_irqs -> cpu.handle_irqs -> (see above)
 
 // called by the cpu
 pub unsafe fn pic_acknowledge_irq() {
-    let irq = match master.requested_irq {
+    let irq = match get_irq(&mut master) {
         Some(i) => i,
         None => return
     };
-    master.requested_irq = None;
 
     if master.irr == 0 {
-        //PIC_LOG_VERBOSE && dbg_log!("master> spurious requested=" + pic.requested_irq);
+        dbg_assert!(false);
+        //PIC_LOG_VERBOSE && dbg_log!("master> spurious requested=" + irq);
         //pic.cpu.pic_call_irq(pic.irq_map | 7);
         return;
     }
@@ -127,16 +132,15 @@ pub unsafe fn pic_acknowledge_irq() {
 }
 
 unsafe fn acknowledge_irq_slave() {
-    let irq = match slave.requested_irq {
+    let irq = match get_irq(&mut slave) {
         Some(i) => i,
         None => return
     };
-    slave.requested_irq = None;
-    master.irq_value &= !(1 << 2);
 
     if slave.irr == 0 {
-        //PIC_LOG_VERBOSE && dbg_log!("slave> spurious requested=" + pic.requested_irq);
+        //PIC_LOG_VERBOSE && dbg_log!("slave> spurious requested=" + irq);
         //pic.cpu.pic_call_irq(pic.irq_map | 7);
+        dbg_assert!(false);
         cpu::pic_call_irq(slave.irq_map | 7);
         return;
     }
@@ -160,22 +164,14 @@ unsafe fn acknowledge_irq_slave() {
     check_irqs(&mut slave);
 }
 
-unsafe fn check_irqs(pic: &mut Pic) {
-    if let Some(irq) = pic.requested_irq {
-        if PIC_LOG_VERBOSE {
-            dbg_log!("[PIC] Already requested irq: {}", irq);
-        }
-        cpu::handle_irqs();
-        return;
-    }
-
+unsafe fn get_irq(pic: &mut Pic) -> Option<u8> {
     let enabled_irr = pic.irr & pic.irq_mask;
 
     if enabled_irr == 0 {
         if PIC_LOG_VERBOSE {
-            dbg_log!("master> no unmasked irrs. irr={:x} mask={:x} isr={:x}", pic.irr, pic.irq_mask, pic.isr);
+            dbg_log!("[PIC] no unmasked irrs. irr={:x} mask={:x} isr={:x}", pic.irr, pic.irq_mask, pic.isr);
         }
-        return;
+        return None
     }
 
     let irq_mask = enabled_irr & (!enabled_irr + 1);
@@ -184,25 +180,38 @@ unsafe fn check_irqs(pic: &mut Pic) {
     if pic.isr != 0 && (pic.isr & (!pic.isr + 1) & special_mask) <= irq_mask {
         // wait for eoi of higher or same priority interrupt
         if PIC_LOG {
-            dbg_log!("[PIC] higher prio: isr={:x} mask={:x} irq={:x}", pic.isr, pic.irq_mask, irq_mask);
+            dbg_log!("[PIC] higher prio: master={} isr={:x} mask={:x} irq={:x}", pic.master, pic.isr, pic.irq_mask, irq_mask);
         }
-        return;
+        return None
     }
 
     dbg_assert!(irq_mask != 0);
     let irq_number = irq_mask.ilog2() as u8;
-    dbg_assert!(irq_mask == (1 << irq_number));
+    dbg_assert!(irq_mask == 1 << irq_number);
 
     if PIC_LOG_VERBOSE {
         dbg_log!("[PIC] request irq {}", irq_number);
     }
 
-    pic.requested_irq = Some(irq_number);
-    // XXX: lifetimes ...
-    if !pic.master {
-        pic_set_irq(2);
+    Some(irq_number)
+}
+
+unsafe fn check_irqs(pic: &mut Pic) {
+    let is_set = get_irq(pic).is_some();
+
+    if pic.master {
+        if is_set {
+            cpu::handle_irqs();
+        }
+    }
+    else {
+        if is_set {
+            pic_set_irq(2);
+        }
+        else {
+            pic_clear_irq(2);
+        }
     }
-    cpu::handle_irqs();
 }
 
 // called by javascript
@@ -274,7 +283,6 @@ unsafe fn port0_write(pic: &mut Pic, v: u8) {
         pic.irq_mask = 0;
         pic.irq_value = 0;
         pic.auto_eoi = true;
-        pic.requested_irq = None;
 
         pic.expect_icw4 = v & 1 != 0;
         pic.state = 1;
@@ -393,3 +401,8 @@ pub unsafe fn port4D1_read() -> u32 { slave.elcr as u32 }
 pub unsafe fn port4D0_write(v: u8) { master.elcr = v }
 #[no_mangle]
 pub unsafe fn port4D1_write(v: u8) { slave.elcr = v }
+
+#[no_mangle]
+pub unsafe fn get_pic_addr_master() -> u32 { std::ptr::addr_of_mut!(master) as u32 }
+#[no_mangle]
+pub unsafe fn get_pic_addr_slave() -> u32 { std::ptr::addr_of_mut!(slave) as u32 }