2013-11-06 01:12:55 +01:00
|
|
|
"use strict";
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
/** @const */
|
|
|
|
var PIC_LOG_VERBOSE = false;
|
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/**
|
2013-11-06 01:12:55 +01:00
|
|
|
* Programmable Interrupt Controller
|
|
|
|
* http://stanislavs.org/helppc/8259.html
|
|
|
|
*
|
|
|
|
* @constructor
|
2015-02-25 18:21:54 +01:00
|
|
|
* @param {CPU} cpu
|
2013-11-06 01:12:55 +01:00
|
|
|
* @param {PIC=} master
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
function PIC(cpu, master)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-05-18 22:18:59 +02:00
|
|
|
/**
|
2014-09-27 04:51:30 +02:00
|
|
|
* 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;
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
this.irq_value = 0;
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
/**
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.requested_irq = -1;
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
this.master = master;
|
|
|
|
this.is_master = this.master === undefined;
|
2014-09-27 04:51:30 +02:00
|
|
|
this.slave = undefined;
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
this.name = this.is_master ? "master" : "slave ";
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
this.expect_icw4 = false;
|
|
|
|
this.state = 0;
|
2016-01-04 04:32:03 +01:00
|
|
|
this.read_isr = 0;
|
2014-09-27 04:51:30 +02:00
|
|
|
this.auto_eoi = 1;
|
2016-01-04 05:25:34 +01:00
|
|
|
this.special_mask_mode = 0;
|
2014-09-27 04:51:30 +02:00
|
|
|
|
2016-01-01 23:16:35 +01:00
|
|
|
this.elcr = 0;
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
this.cpu = cpu;
|
|
|
|
|
|
|
|
// Checking for callable interrupts:
|
2016-08-02 05:30:41 +02:00
|
|
|
// (cpu changes interrupt flag) -> cpu.handle_irqs -> pic.check_irqs -> cpu.pic_call_irq
|
2016-01-04 04:32:03 +01:00
|
|
|
// (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)
|
|
|
|
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
if(this.is_master)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.slave = new PIC(this.cpu, this);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.check_irqs = function()
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
if(this.requested_irq >= 0)
|
|
|
|
{
|
|
|
|
PIC_LOG_VERBOSE && dbg_log("master> Already requested irq: " + this.requested_irq, LOG_PIC);
|
|
|
|
this.cpu.handle_irqs();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
var enabled_irr = this.irr & this.irq_mask;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
|
|
|
if(!enabled_irr)
|
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
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);
|
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
return;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
var irq_mask = enabled_irr & -enabled_irr;
|
2016-01-23 00:00:16 +01:00
|
|
|
var special_mask = this.special_mask_mode ? this.irq_mask : -1;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2016-01-23 00:00:16 +01:00
|
|
|
if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
|
|
|
// wait for eoi of higher or same priority interrupt
|
2016-01-04 04:32:03 +01:00
|
|
|
dbg_log("master> higher prio: isr=" + h(this.isr, 2) +
|
|
|
|
" mask=" + h(this.irq_mask & 0xff, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
|
2017-03-07 23:39:58 +01:00
|
|
|
return;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
dbg_assert(irq_mask !== 0);
|
|
|
|
var irq_number = v86util.int_log2_byte(irq_mask);
|
|
|
|
dbg_assert(irq_mask === (1 << irq_number));
|
2013-11-25 11:58:36 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
PIC_LOG_VERBOSE && dbg_log("master> request irq " + irq_number, LOG_PIC);
|
|
|
|
|
|
|
|
this.requested_irq = irq_number;
|
|
|
|
this.cpu.handle_irqs();
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
this.acknowledge_irq = function()
|
|
|
|
{
|
|
|
|
if(this.requested_irq === -1)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
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;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
dbg_assert(this.irr); // spurious
|
|
|
|
dbg_assert(this.requested_irq >= 0);
|
|
|
|
|
|
|
|
var irq_mask = 1 << this.requested_irq;
|
|
|
|
this.irr &= ~irq_mask; // not in level mode
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
if(!this.auto_eoi)
|
2013-11-25 11:58:36 +01:00
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.isr |= irq_mask;
|
2013-11-25 11:58:36 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
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);
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
this.requested_irq = -1;
|
|
|
|
this.check_irqs();
|
2014-10-21 21:51:42 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// is slave
|
2014-06-15 22:25:17 +02:00
|
|
|
this.check_irqs = function()
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
if(this.requested_irq >= 0)
|
|
|
|
{
|
|
|
|
PIC_LOG_VERBOSE && dbg_log("slave > Already requested irq: " + this.requested_irq, LOG_PIC);
|
|
|
|
this.cpu.handle_irqs();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
var enabled_irr = this.irr & this.irq_mask;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
|
|
|
if(!enabled_irr)
|
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
if(PIC_LOG_VERBOSE)
|
|
|
|
{
|
|
|
|
dbg_log("slave > no unmasked irrs. irr=" + h(this.irr, 2) +
|
2017-03-04 22:00:07 +01:00
|
|
|
" mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
|
2016-01-04 04:32:03 +01:00
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
return;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
var irq_mask = enabled_irr & -enabled_irr;
|
2016-01-23 00:00:16 +01:00
|
|
|
var special_mask = this.special_mask_mode ? this.irq_mask : -1;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2016-01-23 00:00:16 +01:00
|
|
|
if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
|
|
|
// wait for eoi of higher or same priority interrupt
|
2017-03-07 23:39:58 +01:00
|
|
|
PIC_LOG_VERBOSE && dbg_log("slave > higher prio: isr=" + h(this.isr, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
|
|
|
|
return;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
dbg_assert(irq_mask !== 0);
|
|
|
|
var irq_number = v86util.int_log2_byte(irq_mask);
|
|
|
|
dbg_assert(irq_mask === (1 << irq_number));
|
2013-11-25 11:58:36 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
PIC_LOG_VERBOSE && dbg_log("slave > request irq " + irq_number, LOG_PIC);
|
|
|
|
this.requested_irq = irq_number;
|
|
|
|
this.master.set_irq(2);
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
this.acknowledge_irq = function()
|
|
|
|
{
|
|
|
|
if(this.requested_irq === -1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-03-04 22:00:07 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
if(this.irr === 0)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
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;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
dbg_assert(this.irr); // spurious
|
|
|
|
dbg_assert(this.requested_irq >= 0);
|
|
|
|
|
|
|
|
var irq_mask = 1 << this.requested_irq;
|
|
|
|
this.irr &= ~irq_mask; // not in level mode
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
if(!this.auto_eoi)
|
2013-11-25 11:58:36 +01:00
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.isr |= irq_mask;
|
2013-11-25 11:58:36 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
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();
|
2014-10-21 21:51:42 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.dump = function()
|
|
|
|
{
|
2014-09-27 04:51:30 +02:00
|
|
|
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);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
if(this.is_master)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-27 04:51:30 +02:00
|
|
|
this.slave.dump();
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
var io_base;
|
2016-01-01 23:16:35 +01:00
|
|
|
var iobase_high;
|
2014-09-27 04:51:30 +02:00
|
|
|
if(this.is_master)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
|
|
|
io_base = 0x20;
|
2016-01-01 23:16:35 +01:00
|
|
|
iobase_high = 0x4D0;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
io_base = 0xA0;
|
2016-01-01 23:16:35 +01:00
|
|
|
iobase_high = 0x4D1;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2017-03-04 22:30:40 +01:00
|
|
|
this.cpu.io.register_write(io_base, this, this.port20_write);
|
|
|
|
this.cpu.io.register_read(io_base, this, this.port20_read);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-04 22:30:40 +01:00
|
|
|
this.cpu.io.register_write(io_base | 1, this, this.port21_write);
|
|
|
|
this.cpu.io.register_read(io_base | 1, this, this.port21_read);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-04 22:30:40 +01:00
|
|
|
this.cpu.io.register_write(iobase_high, this, this.port4D0_write);
|
|
|
|
this.cpu.io.register_read(iobase_high, this, this.port4D0_read);
|
2016-01-01 23:16:35 +01:00
|
|
|
|
|
|
|
|
2014-09-27 04:51:30 +02:00
|
|
|
if(this.is_master)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.set_irq = function(irq_number)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
|
|
|
dbg_assert(irq_number >= 0 && irq_number < 16);
|
2016-01-04 04:32:03 +01:00
|
|
|
if(PIC_LOG_VERBOSE)
|
|
|
|
{
|
|
|
|
dbg_log("master> set irq " + irq_number, LOG_PIC);
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
|
|
|
if(irq_number >= 8)
|
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.slave.set_irq(irq_number - 8);
|
2017-03-07 23:39:58 +01:00
|
|
|
return;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2016-01-04 04:32:03 +01:00
|
|
|
var irq_mask = 1 << irq_number;
|
2016-11-09 13:42:53 +01:00
|
|
|
if((this.irq_value & irq_mask) === 0)
|
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
this.irr |= irq_mask;
|
2016-11-09 13:42:53 +01:00
|
|
|
this.irq_value |= irq_mask;
|
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2016-01-04 04:32:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2017-03-04 22:00:07 +01:00
|
|
|
return;
|
2016-01-04 04:32:03 +01:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2016-11-09 13:42:53 +01:00
|
|
|
var irq_mask = 1 << irq_number;
|
|
|
|
if(this.irq_value & irq_mask)
|
|
|
|
{
|
2017-03-04 22:30:40 +01:00
|
|
|
this.irq_value &= ~irq_mask;
|
2016-11-09 13:42:53 +01:00
|
|
|
this.irr &= ~irq_mask;
|
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2013-11-06 01:12:55 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.set_irq = function(irq_number)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
|
|
|
dbg_assert(irq_number >= 0 && irq_number < 8);
|
2016-01-04 04:32:03 +01:00
|
|
|
if(PIC_LOG_VERBOSE)
|
|
|
|
{
|
|
|
|
dbg_log("slave > set irq " + irq_number, LOG_PIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
var irq_mask = 1 << irq_number;
|
2016-11-09 13:42:53 +01:00
|
|
|
if((this.irq_value & irq_mask) === 0)
|
|
|
|
{
|
|
|
|
this.irr |= irq_mask;
|
|
|
|
this.irq_value |= irq_mask;
|
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2016-01-04 04:32:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
this.clear_irq = function(irq_number)
|
|
|
|
{
|
2016-11-09 13:42:53 +01:00
|
|
|
dbg_assert(irq_number >= 0 && irq_number < 8);
|
2016-01-04 04:32:03 +01:00
|
|
|
if(PIC_LOG_VERBOSE)
|
|
|
|
{
|
|
|
|
dbg_log("slave > clear irq " + irq_number, LOG_PIC);
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2016-11-09 13:42:53 +01:00
|
|
|
var irq_mask = 1 << irq_number;
|
|
|
|
if(this.irq_value & irq_mask)
|
|
|
|
{
|
|
|
|
this.irq_value &= ~irq_mask;
|
|
|
|
this.irr &= ~irq_mask;
|
|
|
|
}
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2013-11-06 01:12:55 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
this.get_isr = function()
|
|
|
|
{
|
2014-09-27 04:51:30 +02:00
|
|
|
return this.isr;
|
2013-11-06 01:12:55 +01:00
|
|
|
};
|
|
|
|
}
|
2015-05-18 22:18:59 +02:00
|
|
|
|
|
|
|
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;
|
2016-01-04 04:32:03 +01:00
|
|
|
state[8] = this.read_isr;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[9] = this.auto_eoi;
|
|
|
|
|
|
|
|
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 = state[5];
|
|
|
|
this.expect_icw4 = state[6];
|
|
|
|
this.state = state[7];
|
2016-01-04 04:32:03 +01:00
|
|
|
this.read_isr = state[8];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.auto_eoi = state[9];
|
|
|
|
};
|
|
|
|
|
2017-03-04 22:30:40 +01:00
|
|
|
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;
|
2017-03-07 23:39:58 +01:00
|
|
|
this.requested_irq = -1;
|
2017-03-04 22:30:40 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
dbg_log("Unknown eoi: " + h(data_byte), LOG_PIC);
|
|
|
|
// os2 v4
|
|
|
|
//dbg_assert(false);
|
|
|
|
this.isr &= this.isr - 1;
|
|
|
|
}
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2017-03-04 22:30:40 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
PIC.prototype.port20_read = function()
|
|
|
|
{
|
|
|
|
if(this.read_isr)
|
|
|
|
{
|
|
|
|
dbg_log("read port 20h (isr): " + h(this.isr));
|
|
|
|
return this.isr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("read port 20h (irr): " + h(this.irr));
|
|
|
|
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;
|
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
if(PIC_LOG_VERBOSE)
|
|
|
|
{
|
|
|
|
dbg_log("interrupt mask: " + (this.irq_mask & 0xFF).toString(2) +
|
|
|
|
" (" + this.name + ")", LOG_PIC);
|
|
|
|
}
|
2017-03-04 22:30:40 +01:00
|
|
|
|
2017-03-07 23:39:58 +01:00
|
|
|
this.check_irqs();
|
2017-03-04 22:30:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|