123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- "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;
- };
|