jslinux/PIC.js

316 lines
9.9 KiB
JavaScript
Raw Normal View History

/*
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) {
2013-03-23 12:12:08 +08:00
/*
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;
};