316 lines
9.9 KiB
JavaScript
316 lines
9.9 KiB
JavaScript
/*
|
||
JSLinux-deobfuscated - An annotated version of the original JSLinux.
|
||
|
||
Original is Copyright (c) 2011-2012 Fabrice Bellard
|
||
Redistribution or commercial use is prohibited without the author's permission.
|
||
|
||
8259A PIC (Programmable Interrupt Controller) Emulation Code
|
||
|
||
The 8259 combines multiple interrupt input sources into a single
|
||
interrupt output to the host microprocessor, extending the interrupt
|
||
levels available in a system beyond the one or two levels found on the
|
||
processor chip.
|
||
|
||
There are three registers, an Interrupt Mask Register (IMR), an
|
||
Interrupt Request Register (IRR), and an In-Service Register
|
||
(ISR):
|
||
IRR - a mask of the current interrupts that are pending acknowledgement
|
||
ISR - a mask of the interrupts that are pending an EOI
|
||
IMR - a mask of interrupts that should not be sent an acknowledgement
|
||
|
||
End Of Interrupt (EOI) operations support specific EOI, non-specific
|
||
EOI, and auto-EOI. A specific EOI specifies the IRQ level it is
|
||
acknowledging in the ISR. A non-specific EOI resets the IRQ level in
|
||
the ISR. Auto-EOI resets the IRQ level in the ISR immediately after
|
||
the interrupt is acknowledged.
|
||
|
||
After the IBM XT, it was decided that 8 IRQs was not enough.
|
||
The backwards-compatible solution was simply to chain two 8259As together,
|
||
the master and slave PIC.
|
||
|
||
Useful References
|
||
-----------------
|
||
https://en.wikipedia.org/wiki/Programmable_Interrupt_Controller
|
||
https://en.wikipedia.org/wiki/Intel_8259
|
||
http://www.thesatya.com/8259.html
|
||
*/
|
||
|
||
/*
|
||
Common PC arrangements of IRQ lines:
|
||
------------------------------------
|
||
|
||
PC/AT and later systems had two 8259 controllers, master and
|
||
slave. IRQ0 through IRQ7 are the master 8259's interrupt lines, while
|
||
IRQ8 through IRQ15 are the slave 8259's interrupt lines. The labels on
|
||
the pins on an 8259 are IR0 through IR7. IRQ0 through IRQ15 are the
|
||
names of the ISA bus's lines to which the 8259s are attached.
|
||
|
||
Master 8259
|
||
IRQ0 – Intel 8253 or Intel 8254 Programmable Interval Timer, aka the system timer
|
||
IRQ1 – Intel 8042 keyboard controller
|
||
IRQ2 – not assigned in PC/XT; cascaded to slave 8259 INT line in PC/AT
|
||
IRQ3 – 8250 UART serial ports 2 and 4
|
||
IRQ4 – 8250 UART serial ports 1 and 3
|
||
IRQ5 – hard disk controller in PC/XT; Intel 8255 parallel ports 2 and 3 in PC/AT
|
||
IRQ6 – Intel 82072A floppy disk controller
|
||
IRQ7 – Intel 8255 parallel port 1 / spurious interrupt
|
||
|
||
Slave 8259 (PC/AT and later only)
|
||
IRQ8 – real-time clock (RTC)
|
||
IRQ9 – no common assignment, but 8-bit cards' IRQ2 line is routed to this interrupt.
|
||
IRQ10 – no common assignment
|
||
IRQ11 – no common assignment
|
||
IRQ12 – Intel 8042 PS/2 mouse controller
|
||
IRQ13 – math coprocessor
|
||
IRQ14 – hard disk controller 1
|
||
IRQ15 – hard disk controller 2
|
||
*/
|
||
|
||
function PIC(PC, port_num) {
|
||
PC.register_ioport_write(port_num, 2, 1, this.ioport_write.bind(this));
|
||
PC.register_ioport_read(port_num, 2, 1, this.ioport_read.bind(this));
|
||
this.reset();
|
||
}
|
||
PIC.prototype.reset = function() {
|
||
this.last_irr = 0;
|
||
this.irr = 0; //Interrupt Request Register
|
||
this.imr = 0; //Interrupt Mask Register
|
||
this.isr = 0; //In-Service Register
|
||
this.priority_add = 0;
|
||
this.irq_base = 0;
|
||
this.read_reg_select = 0;
|
||
this.special_mask = 0;
|
||
this.init_state = 0;
|
||
this.auto_eoi = 0;
|
||
this.rotate_on_autoeoi = 0;
|
||
this.init4 = 0;
|
||
this.elcr = 0; // Edge/Level Control Register
|
||
this.elcr_mask = 0;
|
||
};
|
||
PIC.prototype.set_irq1 = function(irq, Qf) {
|
||
var ir_register;
|
||
ir_register = 1 << irq;
|
||
if (Qf) {
|
||
if ((this.last_irr & ir_register) == 0)
|
||
this.irr |= ir_register;
|
||
this.last_irr |= ir_register;
|
||
} else {
|
||
this.last_irr &= ~ir_register;
|
||
}
|
||
};
|
||
/*
|
||
The priority assignments for IRQ0-7 seem to be maintained in a
|
||
cyclic order modulo 8 by the 8259A. On bootup, it default to:
|
||
|
||
Priority: 0 1 2 3 4 5 6 7
|
||
IRQ: 7 6 5 4 3 2 1 0
|
||
|
||
but can be rotated automatically or programmatically to a state e.g.:
|
||
|
||
Priority: 5 6 7 0 1 2 3 4
|
||
IRQ: 7 6 5 4 3 2 1 0
|
||
*/
|
||
PIC.prototype.get_priority = function(ir_register) {
|
||
var priority;
|
||
if (ir_register == 0)
|
||
return -1;
|
||
priority = 7;
|
||
while ((ir_register & (1 << ((priority + this.priority_add) & 7))) == 0)
|
||
priority--;
|
||
return priority;
|
||
};
|
||
PIC.prototype.get_irq = function() {
|
||
var ir_register, in_service_priority, priority;
|
||
ir_register = this.irr & ~this.imr;
|
||
priority = this.get_priority(ir_register);
|
||
if (priority < 0)
|
||
return -1;
|
||
in_service_priority = this.get_priority(this.isr);
|
||
if (priority > in_service_priority) {
|
||
return priority;
|
||
} else {
|
||
return -1;
|
||
}
|
||
};
|
||
PIC.prototype.intack = function(irq) {
|
||
if (this.auto_eoi) {
|
||
if (this.rotate_on_auto_eoi)
|
||
this.priority_add = (irq + 1) & 7;
|
||
} else {
|
||
this.isr |= (1 << irq);
|
||
}
|
||
if (!(this.elcr & (1 << irq)))
|
||
this.irr &= ~(1 << irq);
|
||
};
|
||
PIC.prototype.ioport_write = function(mem8_loc, x) {
|
||
var priority;
|
||
mem8_loc &= 1;
|
||
if (mem8_loc == 0) {
|
||
if (x & 0x10) {
|
||
/*
|
||
ICW1
|
||
// 7:5 = address (if MCS-80/85 mode)
|
||
// 4 == 1
|
||
// 3: 1 == level triggered, 0 == edge triggered
|
||
// 2: 1 == call interval 4, 0 == call interval 8
|
||
// 1: 1 == single PIC, 0 == cascaded PICs
|
||
// 0: 1 == send ICW4
|
||
|
||
*/
|
||
this.reset();
|
||
this.init_state = 1;
|
||
this.init4 = x & 1;
|
||
if (x & 0x02)
|
||
throw "single mode not supported";
|
||
if (x & 0x08)
|
||
throw "level sensitive irq not supported";
|
||
} else if (x & 0x08) {
|
||
if (x & 0x02)
|
||
this.read_reg_select = x & 1;
|
||
if (x & 0x40)
|
||
this.special_mask = (x >> 5) & 1;
|
||
} else {
|
||
switch (x) {
|
||
case 0x00:
|
||
case 0x80:
|
||
this.rotate_on_autoeoi = x >> 7;
|
||
break;
|
||
case 0x20:
|
||
case 0xa0:
|
||
priority = this.get_priority(this.isr);
|
||
if (priority >= 0) {
|
||
this.isr &= ~(1 << ((priority + this.priority_add) & 7));
|
||
}
|
||
if (x == 0xa0)
|
||
this.priority_add = (this.priority_add + 1) & 7;
|
||
break;
|
||
case 0x60:
|
||
case 0x61:
|
||
case 0x62:
|
||
case 0x63:
|
||
case 0x64:
|
||
case 0x65:
|
||
case 0x66:
|
||
case 0x67:
|
||
priority = x & 7;
|
||
this.isr &= ~(1 << priority);
|
||
break;
|
||
case 0xc0:
|
||
case 0xc1:
|
||
case 0xc2:
|
||
case 0xc3:
|
||
case 0xc4:
|
||
case 0xc5:
|
||
case 0xc6:
|
||
case 0xc7:
|
||
this.priority_add = (x + 1) & 7;
|
||
break;
|
||
case 0xe0:
|
||
case 0xe1:
|
||
case 0xe2:
|
||
case 0xe3:
|
||
case 0xe4:
|
||
case 0xe5:
|
||
case 0xe6:
|
||
case 0xe7:
|
||
priority = x & 7;
|
||
this.isr &= ~(1 << priority);
|
||
this.priority_add = (priority + 1) & 7;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
switch (this.init_state) {
|
||
case 0:
|
||
this.imr = x;
|
||
this.update_irq();
|
||
break;
|
||
case 1:
|
||
this.irq_base = x & 0xf8;
|
||
this.init_state = 2;
|
||
break;
|
||
case 2:
|
||
if (this.init4) {
|
||
this.init_state = 3;
|
||
} else {
|
||
this.init_state = 0;
|
||
}
|
||
break;
|
||
case 3:
|
||
this.auto_eoi = (x >> 1) & 1;
|
||
this.init_state = 0;
|
||
break;
|
||
}
|
||
}
|
||
};
|
||
PIC.prototype.ioport_read = function(Ug) {
|
||
var mem8_loc, return_register;
|
||
mem8_loc = Ug & 1;
|
||
if (mem8_loc == 0) {
|
||
if (this.read_reg_select)
|
||
return_register = this.isr;
|
||
else
|
||
return_register = this.irr;
|
||
} else {
|
||
return_register = this.imr;
|
||
}
|
||
return return_register;
|
||
};
|
||
|
||
|
||
function PIC_Controller(PC, master_PIC_port, slave_PIC_port, cpu_set_irq_callback) {
|
||
this.pics = new Array();
|
||
this.pics[0] = new PIC(PC, master_PIC_port);
|
||
this.pics[1] = new PIC(PC, slave_PIC_port);
|
||
this.pics[0].elcr_mask = 0xf8;
|
||
this.pics[1].elcr_mask = 0xde;
|
||
this.irq_requested = 0;
|
||
this.cpu_set_irq = cpu_set_irq_callback;
|
||
this.pics[0].update_irq = this.update_irq.bind(this);
|
||
this.pics[1].update_irq = this.update_irq.bind(this);
|
||
}
|
||
PIC_Controller.prototype.update_irq = function() {
|
||
var slave_irq, irq;
|
||
slave_irq = this.pics[1].get_irq();
|
||
if (slave_irq >= 0) {
|
||
this.pics[0].set_irq1(2, 1);
|
||
this.pics[0].set_irq1(2, 0);
|
||
}
|
||
irq = this.pics[0].get_irq();
|
||
if (irq >= 0) {
|
||
this.cpu_set_irq(1);
|
||
} else {
|
||
this.cpu_set_irq(0);
|
||
}
|
||
};
|
||
PIC_Controller.prototype.set_irq = function(irq, Qf) {
|
||
this.pics[irq >> 3].set_irq1(irq & 7, Qf);
|
||
this.update_irq();
|
||
};
|
||
PIC_Controller.prototype.get_hard_intno = function() {
|
||
var irq, slave_irq, intno;
|
||
irq = this.pics[0].get_irq();
|
||
if (irq >= 0) {
|
||
this.pics[0].intack(irq);
|
||
if (irq == 2) { //IRQ 2 cascaded to slave 8259 INT line in PC/AT
|
||
slave_irq = this.pics[1].get_irq();
|
||
if (slave_irq >= 0) {
|
||
this.pics[1].intack(slave_irq);
|
||
} else {
|
||
slave_irq = 7;
|
||
}
|
||
intno = this.pics[1].irq_base + slave_irq;
|
||
irq = slave_irq + 8;
|
||
} else {
|
||
intno = this.pics[0].irq_base + irq;
|
||
}
|
||
} else {
|
||
irq = 7;
|
||
intno = this.pics[0].irq_base + irq;
|
||
}
|
||
this.update_irq();
|
||
return intno;
|
||
};
|
||
|
||
|