-
Notifications
You must be signed in to change notification settings - Fork 146
/
PIC.js
315 lines (295 loc) · 9.88 KB
/
PIC.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*
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;
};