pic.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. "use strict";
  2. /** @const */
  3. var PIC_LOG_VERBOSE = false;
  4. /**
  5. * Programmable Interrupt Controller
  6. * http://stanislavs.org/helppc/8259.html
  7. *
  8. * @constructor
  9. * @param {CPU} cpu
  10. * @param {PIC=} master
  11. */
  12. function PIC(cpu, master)
  13. {
  14. /**
  15. * all irqs off
  16. * @type {number}
  17. */
  18. this.irq_mask = 0;
  19. /**
  20. * @type {number}
  21. *
  22. * Bogus default value (both master and slave mapped to 0).
  23. * Will be initialized by the BIOS
  24. */
  25. this.irq_map = 0;
  26. /**
  27. * in-service register
  28. * Holds interrupts that are currently being serviced
  29. * @type {number}
  30. */
  31. this.isr = 0;
  32. /**
  33. * interrupt request register
  34. * Holds interrupts that have been requested
  35. * @type {number}
  36. */
  37. this.irr = 0;
  38. this.irq_value = 0;
  39. /**
  40. * @type {number}
  41. */
  42. this.requested_irq = -1;
  43. this.master = master;
  44. this.is_master = this.master === undefined;
  45. this.slave = undefined;
  46. this.name = this.is_master ? "master" : "slave ";
  47. this.expect_icw4 = false;
  48. this.state = 0;
  49. this.read_isr = 0;
  50. this.auto_eoi = 1;
  51. this.special_mask_mode = 0;
  52. this.elcr = 0;
  53. this.cpu = cpu;
  54. // Checking for callable interrupts:
  55. // (cpu changes interrupt flag) -> cpu.handle_irqs -> pic.check_irqs -> cpu.pic_call_irq
  56. // (pic changes isr/irr) -> cpu.handle_irqs -> ...
  57. // triggering irqs:
  58. // (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> cpu.handle_irqs -> (see above)
  59. if(this.is_master)
  60. {
  61. this.slave = new PIC(this.cpu, this);
  62. this.check_irqs = function()
  63. {
  64. if(this.requested_irq >= 0)
  65. {
  66. PIC_LOG_VERBOSE && dbg_log("master> Already requested irq: " + this.requested_irq, LOG_PIC);
  67. this.cpu.handle_irqs();
  68. return;
  69. }
  70. var enabled_irr = this.irr & this.irq_mask;
  71. if(!enabled_irr)
  72. {
  73. if(PIC_LOG_VERBOSE)
  74. {
  75. dbg_log("master> no unmasked irrs. irr=" + h(this.irr, 2) +
  76. " mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
  77. }
  78. return;
  79. }
  80. var irq_mask = enabled_irr & -enabled_irr;
  81. var special_mask = this.special_mask_mode ? this.irq_mask : -1;
  82. if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
  83. {
  84. // wait for eoi of higher or same priority interrupt
  85. dbg_log("master> higher prio: isr=" + h(this.isr, 2) +
  86. " mask=" + h(this.irq_mask & 0xff, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
  87. return;
  88. }
  89. dbg_assert(irq_mask !== 0);
  90. var irq_number = v86util.int_log2_byte(irq_mask);
  91. dbg_assert(irq_mask === (1 << irq_number));
  92. PIC_LOG_VERBOSE && dbg_log("master> request irq " + irq_number, LOG_PIC);
  93. this.requested_irq = irq_number;
  94. this.cpu.handle_irqs();
  95. };
  96. this.acknowledge_irq = function()
  97. {
  98. if(this.requested_irq === -1)
  99. {
  100. return;
  101. }
  102. if(this.irr === 0)
  103. {
  104. PIC_LOG_VERBOSE && dbg_log("master> spurious requested=" + this.requested_irq, LOG_PIC);
  105. this.requested_irq = -1;
  106. //this.cpu.pic_call_irq(this.irq_map | 7);
  107. return;
  108. }
  109. dbg_assert(this.irr); // spurious
  110. dbg_assert(this.requested_irq >= 0);
  111. var irq_mask = 1 << this.requested_irq;
  112. if((this.elcr & irq_mask) === 0) // not in level mode
  113. {
  114. this.irr &= ~irq_mask;
  115. }
  116. if(!this.auto_eoi)
  117. {
  118. this.isr |= irq_mask;
  119. }
  120. PIC_LOG_VERBOSE && dbg_log("master> acknowledge " + this.requested_irq, LOG_PIC);
  121. if(this.requested_irq === 2)
  122. {
  123. this.slave.acknowledge_irq();
  124. }
  125. else
  126. {
  127. this.cpu.pic_call_irq(this.irq_map | this.requested_irq);
  128. }
  129. this.requested_irq = -1;
  130. this.check_irqs();
  131. };
  132. }
  133. else
  134. {
  135. // is slave
  136. this.check_irqs = function()
  137. {
  138. if(this.requested_irq >= 0)
  139. {
  140. PIC_LOG_VERBOSE && dbg_log("slave > Already requested irq: " + this.requested_irq, LOG_PIC);
  141. this.cpu.handle_irqs();
  142. return;
  143. }
  144. var enabled_irr = this.irr & this.irq_mask;
  145. if(!enabled_irr)
  146. {
  147. if(PIC_LOG_VERBOSE)
  148. {
  149. dbg_log("slave > no unmasked irrs. irr=" + h(this.irr, 2) +
  150. " mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
  151. }
  152. return;
  153. }
  154. var irq_mask = enabled_irr & -enabled_irr;
  155. var special_mask = this.special_mask_mode ? this.irq_mask : -1;
  156. if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
  157. {
  158. // wait for eoi of higher or same priority interrupt
  159. PIC_LOG_VERBOSE && dbg_log("slave > higher prio: isr=" + h(this.isr, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
  160. return;
  161. }
  162. dbg_assert(irq_mask !== 0);
  163. var irq_number = v86util.int_log2_byte(irq_mask);
  164. dbg_assert(irq_mask === (1 << irq_number));
  165. PIC_LOG_VERBOSE && dbg_log("slave > request irq " + irq_number, LOG_PIC);
  166. this.requested_irq = irq_number;
  167. this.master.set_irq(2);
  168. };
  169. this.acknowledge_irq = function()
  170. {
  171. if(this.requested_irq === -1)
  172. {
  173. return;
  174. }
  175. if(this.irr === 0)
  176. {
  177. PIC_LOG_VERBOSE && dbg_log("slave > spurious requested=" + this.requested_irq, LOG_PIC);
  178. this.requested_irq = -1;
  179. this.master.irq_value &= ~(1 << 2);
  180. this.cpu.pic_call_irq(this.irq_map | 7);
  181. return;
  182. }
  183. dbg_assert(this.irr); // spurious
  184. dbg_assert(this.requested_irq >= 0);
  185. var irq_mask = 1 << this.requested_irq;
  186. if((this.elcr & irq_mask) === 0) // not in level mode
  187. {
  188. this.irr &= ~irq_mask;
  189. }
  190. if(!this.auto_eoi)
  191. {
  192. this.isr |= irq_mask;
  193. }
  194. this.master.irq_value &= ~(1 << 2);
  195. PIC_LOG_VERBOSE && dbg_log("slave > acknowledge " + this.requested_irq, LOG_PIC);
  196. this.cpu.pic_call_irq(this.irq_map | this.requested_irq);
  197. this.requested_irq = -1;
  198. this.check_irqs();
  199. };
  200. }
  201. this.dump = function()
  202. {
  203. dbg_log("mask: " + h(this.irq_mask & 0xFF), LOG_PIC);
  204. dbg_log("base: " + h(this.irq_map), LOG_PIC);
  205. dbg_log("requested: " + h(this.irr), LOG_PIC);
  206. dbg_log("serviced: " + h(this.isr), LOG_PIC);
  207. if(this.is_master)
  208. {
  209. this.slave.dump();
  210. }
  211. };
  212. var io_base;
  213. var iobase_high;
  214. if(this.is_master)
  215. {
  216. io_base = 0x20;
  217. iobase_high = 0x4D0;
  218. }
  219. else
  220. {
  221. io_base = 0xA0;
  222. iobase_high = 0x4D1;
  223. }
  224. this.cpu.io.register_write(io_base, this, this.port20_write);
  225. this.cpu.io.register_read(io_base, this, this.port20_read);
  226. this.cpu.io.register_write(io_base | 1, this, this.port21_write);
  227. this.cpu.io.register_read(io_base | 1, this, this.port21_read);
  228. this.cpu.io.register_write(iobase_high, this, this.port4D0_write);
  229. this.cpu.io.register_read(iobase_high, this, this.port4D0_read);
  230. if(this.is_master)
  231. {
  232. this.set_irq = function(irq_number)
  233. {
  234. dbg_assert(irq_number >= 0 && irq_number < 16);
  235. if(irq_number >= 8)
  236. {
  237. this.slave.set_irq(irq_number - 8);
  238. return;
  239. }
  240. var irq_mask = 1 << irq_number;
  241. if((this.irq_value & irq_mask) === 0)
  242. {
  243. if(PIC_LOG_VERBOSE)
  244. {
  245. dbg_log("master> set irq " + irq_number, LOG_PIC);
  246. }
  247. this.irr |= irq_mask;
  248. this.irq_value |= irq_mask;
  249. this.check_irqs();
  250. }
  251. else
  252. {
  253. if(PIC_LOG_VERBOSE)
  254. {
  255. dbg_log("master> set irq " + irq_number + ": already set!", LOG_PIC);
  256. }
  257. }
  258. };
  259. this.clear_irq = function(irq_number)
  260. {
  261. dbg_assert(irq_number >= 0 && irq_number < 16);
  262. if(PIC_LOG_VERBOSE)
  263. {
  264. dbg_log("master> clear irq " + irq_number, LOG_PIC);
  265. }
  266. if(irq_number >= 8)
  267. {
  268. this.slave.clear_irq(irq_number - 8);
  269. return;
  270. }
  271. var irq_mask = 1 << irq_number;
  272. if(this.irq_value & irq_mask)
  273. {
  274. this.irq_value &= ~irq_mask;
  275. this.irr &= ~irq_mask;
  276. this.check_irqs();
  277. }
  278. };
  279. }
  280. else
  281. {
  282. this.set_irq = function(irq_number)
  283. {
  284. dbg_assert(irq_number >= 0 && irq_number < 8);
  285. var irq_mask = 1 << irq_number;
  286. if((this.irq_value & irq_mask) === 0)
  287. {
  288. if(PIC_LOG_VERBOSE)
  289. {
  290. dbg_log("slave > set irq " + irq_number, LOG_PIC);
  291. }
  292. this.irr |= irq_mask;
  293. this.irq_value |= irq_mask;
  294. this.check_irqs();
  295. }
  296. else
  297. {
  298. if(PIC_LOG_VERBOSE)
  299. {
  300. dbg_log("slave > set irq " + irq_number + ": already set!", LOG_PIC);
  301. }
  302. }
  303. };
  304. this.clear_irq = function(irq_number)
  305. {
  306. dbg_assert(irq_number >= 0 && irq_number < 8);
  307. if(PIC_LOG_VERBOSE)
  308. {
  309. dbg_log("slave > clear irq " + irq_number, LOG_PIC);
  310. }
  311. var irq_mask = 1 << irq_number;
  312. if(this.irq_value & irq_mask)
  313. {
  314. this.irq_value &= ~irq_mask;
  315. this.irr &= ~irq_mask;
  316. this.check_irqs();
  317. }
  318. };
  319. }
  320. this.get_isr = function()
  321. {
  322. return this.isr;
  323. };
  324. }
  325. PIC.prototype.get_state = function()
  326. {
  327. var state = [];
  328. state[0] = this.irq_mask;
  329. state[1] = this.irq_map;
  330. state[2] = this.isr;
  331. state[3] = this.irr;
  332. state[4] = this.is_master;
  333. state[5] = this.slave;
  334. state[6] = this.expect_icw4;
  335. state[7] = this.state;
  336. state[8] = this.read_isr;
  337. state[9] = this.auto_eoi;
  338. state[10] = this.elcr;
  339. return state;
  340. };
  341. PIC.prototype.set_state = function(state)
  342. {
  343. this.irq_mask = state[0];
  344. this.irq_map = state[1];
  345. this.isr = state[2];
  346. this.irr = state[3];
  347. this.is_master = state[4];
  348. this.slave && this.slave.set_state(state[5]);
  349. this.expect_icw4 = state[6];
  350. this.state = state[7];
  351. this.read_isr = state[8];
  352. this.auto_eoi = state[9];
  353. this.elcr = state[10];
  354. };
  355. PIC.prototype.port20_write = function(data_byte)
  356. {
  357. //dbg_log("20 write: " + h(data_byte), LOG_PIC);
  358. if(data_byte & 0x10) // xxxx1xxx
  359. {
  360. // icw1
  361. dbg_log("icw1 = " + h(data_byte), LOG_PIC);
  362. this.isr = 0;
  363. this.irr = 0;
  364. this.irq_mask = 0;
  365. this.irq_value = 0;
  366. this.auto_eoi = 1;
  367. this.requested_irq = -1;
  368. this.expect_icw4 = data_byte & 1;
  369. this.state = 1;
  370. }
  371. else if(data_byte & 8) // xxx01xxx
  372. {
  373. // ocw3
  374. dbg_log("ocw3: " + h(data_byte), LOG_PIC);
  375. if(data_byte & 2)
  376. {
  377. this.read_isr = data_byte & 1;
  378. }
  379. if(data_byte & 4)
  380. {
  381. dbg_assert(false, "unimplemented: polling", LOG_PIC);
  382. }
  383. if(data_byte & 0x40)
  384. {
  385. this.special_mask_mode = (data_byte & 0x20) === 0x20;
  386. dbg_log("special mask mode: " + this.special_mask_mode, LOG_PIC);
  387. }
  388. }
  389. else // xxx00xxx
  390. {
  391. // ocw2
  392. // end of interrupt
  393. dbg_log("eoi: " + h(data_byte) + " (" + this.name + ")", LOG_PIC);
  394. var eoi_type = data_byte >> 5;
  395. if(eoi_type === 1)
  396. {
  397. // non-specific eoi
  398. this.isr &= this.isr - 1;
  399. dbg_log("new isr: " + h(this.isr, 2), LOG_PIC);
  400. }
  401. else if(eoi_type === 3)
  402. {
  403. // specific eoi
  404. this.isr &= ~(1 << (data_byte & 7));
  405. }
  406. else if((data_byte & 0xC8) === 0xC0)
  407. {
  408. // os2 v4
  409. let priority = data_byte & 7;
  410. dbg_log("lowest priority: " + h(priority), LOG_PIC);
  411. }
  412. else
  413. {
  414. dbg_log("Unknown eoi: " + h(data_byte), LOG_PIC);
  415. dbg_assert(false);
  416. this.isr &= this.isr - 1;
  417. }
  418. this.check_irqs();
  419. }
  420. };
  421. PIC.prototype.port20_read = function()
  422. {
  423. if(this.read_isr)
  424. {
  425. dbg_log("read port 20h (isr): " + h(this.isr), LOG_PIC);
  426. return this.isr;
  427. }
  428. else
  429. {
  430. dbg_log("read port 20h (irr): " + h(this.irr), LOG_PIC);
  431. return this.irr;
  432. }
  433. };
  434. PIC.prototype.port21_write = function(data_byte)
  435. {
  436. //dbg_log("21 write: " + h(data_byte), LOG_PIC);
  437. if(this.state === 0)
  438. {
  439. if(this.expect_icw4)
  440. {
  441. // icw4
  442. this.expect_icw4 = false;
  443. this.auto_eoi = data_byte & 2;
  444. dbg_log("icw4: " + h(data_byte) + " autoeoi=" + this.auto_eoi, LOG_PIC);
  445. if((data_byte & 1) === 0)
  446. {
  447. dbg_assert(false, "unimplemented: not 8086 mode", LOG_PIC);
  448. }
  449. }
  450. else
  451. {
  452. // ocw1
  453. this.irq_mask = ~data_byte;
  454. if(PIC_LOG_VERBOSE)
  455. {
  456. dbg_log("interrupt mask: " + (this.irq_mask & 0xFF).toString(2) +
  457. " (" + this.name + ")", LOG_PIC);
  458. }
  459. this.check_irqs();
  460. }
  461. }
  462. else if(this.state === 1)
  463. {
  464. // icw2
  465. this.irq_map = data_byte;
  466. dbg_log("interrupts are mapped to " + h(this.irq_map) +
  467. " (" + this.name + ")", LOG_PIC);
  468. this.state++;
  469. }
  470. else if(this.state === 2)
  471. {
  472. // icw3
  473. this.state = 0;
  474. dbg_log("icw3: " + h(data_byte), LOG_PIC);
  475. }
  476. };
  477. PIC.prototype.port21_read = function()
  478. {
  479. dbg_log("21h read " + h(~this.irq_mask & 0xff), LOG_PIC);
  480. return ~this.irq_mask & 0xFF;
  481. };
  482. PIC.prototype.port4D0_read = function()
  483. {
  484. dbg_log("elcr read: " + h(this.elcr, 2), LOG_PIC);
  485. return this.elcr;
  486. };
  487. PIC.prototype.port4D0_write = function(value)
  488. {
  489. dbg_log("elcr write: " + h(value, 2), LOG_PIC);
  490. // set by seabios to 00 0C (only set for pci interrupts)
  491. this.elcr = value;
  492. };