pic.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. this.master = master;
  40. this.is_master = this.master === undefined;
  41. this.slave = undefined;
  42. this.name = this.is_master ? "master" : "slave ";
  43. this.expect_icw4 = false;
  44. this.state = 0;
  45. this.read_isr = 0;
  46. this.auto_eoi = 1;
  47. this.special_mask_mode = 0;
  48. this.elcr = 0;
  49. this.cpu = cpu;
  50. // Checking for callable interrupts:
  51. // (cpu changes interrupt flag) -> cpu.handle_irqs -> pic.check_irqs -> cpu.pic_call_irq
  52. // (pic changes isr/irr) -> cpu.handle_irqs -> ...
  53. // triggering irqs:
  54. // (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> cpu.handle_irqs -> (see above)
  55. if(this.is_master)
  56. {
  57. this.slave = new PIC(this.cpu, this);
  58. this.check_irqs = function()
  59. {
  60. var enabled_irr = this.irr & this.irq_mask;
  61. if(!enabled_irr)
  62. {
  63. if(PIC_LOG_VERBOSE)
  64. {
  65. dbg_log("master> no unmasked irrs. irr=" + h(this.irr, 2) +
  66. " mask=" + h(this.irq_mask & 0xff, 2) + " isr=" + h(this.isr, 2), LOG_PIC);
  67. }
  68. return this.slave.check_irqs();
  69. }
  70. var irq_mask = enabled_irr & -enabled_irr;
  71. var special_mask = this.special_mask_mode ? this.irq_mask : -1;
  72. if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
  73. {
  74. // wait for eoi of higher or same priority interrupt
  75. dbg_log("master> higher prio: isr=" + h(this.isr, 2) +
  76. " mask=" + h(this.irq_mask & 0xff, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
  77. return false;
  78. }
  79. dbg_assert(irq_mask !== 0);
  80. var irq_number = v86util.int_log2_byte(irq_mask);
  81. dbg_assert(irq_mask === (1 << irq_number));
  82. this.irr &= ~irq_mask; // not in level mode
  83. if(irq_number === 2)
  84. {
  85. // this should always return true
  86. this.irq_value &= ~2;
  87. var did = this.slave.check_irqs();
  88. if(!did) dbg_log("Slave had irq when master irq number was 2", LOG_PIC);
  89. return did;
  90. }
  91. if(!this.auto_eoi)
  92. {
  93. this.isr |= irq_mask;
  94. }
  95. dbg_log("master handling irq " + irq_number, LOG_PIC);
  96. this.cpu.pic_call_irq(this.irq_map | irq_number);
  97. return true;
  98. };
  99. }
  100. else
  101. {
  102. // is slave
  103. this.check_irqs = function()
  104. {
  105. var enabled_irr = this.irr & this.irq_mask;
  106. if(!enabled_irr)
  107. {
  108. if(PIC_LOG_VERBOSE)
  109. {
  110. dbg_log("slave > no unmasked irrs. irr=" + h(this.irr, 2) +
  111. " mask=" + h(this.irq_mask & 0xff, 2), LOG_PIC);
  112. }
  113. return false;
  114. }
  115. var irq_mask = enabled_irr & -enabled_irr;
  116. var special_mask = this.special_mask_mode ? this.irq_mask : -1;
  117. if(this.isr && (this.isr & -this.isr & special_mask) <= irq_mask)
  118. {
  119. // wait for eoi of higher or same priority interrupt
  120. dbg_log("slave > higher prio: isr=" + h(this.isr, 2) + " irq=" + h(irq_mask, 2), LOG_PIC);
  121. return false;
  122. }
  123. dbg_assert(irq_mask !== 0);
  124. var irq_number = v86util.int_log2_byte(irq_mask);
  125. dbg_assert(irq_mask === (1 << irq_number));
  126. this.irr &= ~irq_mask; // not in level mode
  127. dbg_log("slave > handling irq " + irq_number, LOG_PIC);
  128. this.cpu.pic_call_irq(this.irq_map | irq_number);
  129. if(this.irr)
  130. {
  131. // tell the master we have one more
  132. this.master.set_irq(2);
  133. }
  134. this.master.clear_irq(2);
  135. if(!this.auto_eoi)
  136. {
  137. this.isr |= irq_mask;
  138. this.master.isr |= 1 << 2;
  139. }
  140. return true;
  141. };
  142. }
  143. this.dump = function()
  144. {
  145. dbg_log("mask: " + h(this.irq_mask & 0xFF), LOG_PIC);
  146. dbg_log("base: " + h(this.irq_map), LOG_PIC);
  147. dbg_log("requested: " + h(this.irr), LOG_PIC);
  148. dbg_log("serviced: " + h(this.isr), LOG_PIC);
  149. if(this.is_master)
  150. {
  151. this.slave.dump();
  152. }
  153. };
  154. var io_base;
  155. var iobase_high;
  156. if(this.is_master)
  157. {
  158. io_base = 0x20;
  159. iobase_high = 0x4D0;
  160. }
  161. else
  162. {
  163. io_base = 0xA0;
  164. iobase_high = 0x4D1;
  165. }
  166. this.cpu.io.register_write(io_base, this, port20_write);
  167. this.cpu.io.register_read(io_base, this, port20_read);
  168. this.cpu.io.register_write(io_base | 1, this, port21_write);
  169. this.cpu.io.register_read(io_base | 1, this, port21_read);
  170. this.cpu.io.register_write(iobase_high, this, port4D0_write);
  171. this.cpu.io.register_read(iobase_high, this, port4D0_read);
  172. function port20_write(data_byte)
  173. {
  174. //dbg_log("20 write: " + h(data_byte), LOG_PIC);
  175. if(data_byte & 0x10) // xxxx1xxx
  176. {
  177. // icw1
  178. dbg_log("icw1 = " + h(data_byte), LOG_PIC);
  179. this.isr = 0;
  180. this.irr = 0;
  181. this.irq_mask = 0;
  182. this.irq_value = 0;
  183. this.auto_eoi = 1;
  184. this.expect_icw4 = data_byte & 1;
  185. this.state = 1;
  186. }
  187. else if(data_byte & 8) // xxx01xxx
  188. {
  189. // ocw3
  190. dbg_log("ocw3: " + h(data_byte), LOG_PIC);
  191. if(data_byte & 2)
  192. {
  193. this.read_isr = data_byte & 1;
  194. }
  195. if(data_byte & 4)
  196. {
  197. dbg_assert(false, "unimplemented: polling", LOG_PIC);
  198. }
  199. if(data_byte & 0x40)
  200. {
  201. this.special_mask_mode = (data_byte & 0x20) === 0x20;
  202. dbg_log("special mask mode: " + this.special_mask_mode, LOG_PIC);
  203. }
  204. }
  205. else // xxx00xxx
  206. {
  207. // ocw2
  208. // end of interrupt
  209. dbg_log("eoi: " + h(data_byte) + " (" + this.name + ")", LOG_PIC);
  210. var eoi_type = data_byte >> 5;
  211. if(eoi_type === 1)
  212. {
  213. // non-specific eoi
  214. this.isr &= this.isr - 1;
  215. dbg_log("new isr: " + h(this.isr, 2), LOG_PIC);
  216. }
  217. else if(eoi_type === 3)
  218. {
  219. // specific eoi
  220. this.isr &= ~(1 << (data_byte & 7));
  221. }
  222. else
  223. {
  224. dbg_log("Unknown eoi: " + h(data_byte), LOG_PIC);
  225. // os2 v4
  226. //dbg_assert(false);
  227. this.isr &= this.isr - 1;
  228. }
  229. this.cpu.handle_irqs();
  230. }
  231. };
  232. function port20_read()
  233. {
  234. if(this.read_isr)
  235. {
  236. return this.isr;
  237. }
  238. else
  239. {
  240. return this.irr;
  241. }
  242. }
  243. function port21_write(data_byte)
  244. {
  245. //dbg_log("21 write: " + h(data_byte), LOG_PIC);
  246. if(this.state === 0)
  247. {
  248. if(this.expect_icw4)
  249. {
  250. // icw4
  251. this.expect_icw4 = false;
  252. this.auto_eoi = data_byte & 2;
  253. dbg_log("icw4: " + h(data_byte) + " autoeoi=" + this.auto_eoi, LOG_PIC);
  254. if((data_byte & 1) === 0)
  255. {
  256. dbg_assert(false, "unimplemented: not 8086 mode", LOG_PIC);
  257. }
  258. }
  259. else
  260. {
  261. // ocw1
  262. this.irq_mask = ~data_byte;
  263. //dbg_log("interrupt mask: " + (this.irq_mask & 0xFF).toString(2) +
  264. // " (" + this.name + ")", LOG_PIC);
  265. this.cpu.handle_irqs();
  266. }
  267. }
  268. else if(this.state === 1)
  269. {
  270. // icw2
  271. this.irq_map = data_byte;
  272. dbg_log("interrupts are mapped to " + h(this.irq_map) +
  273. " (" + this.name + ")", LOG_PIC);
  274. this.state++;
  275. }
  276. else if(this.state === 2)
  277. {
  278. // icw3
  279. this.state = 0;
  280. dbg_log("icw3: " + h(data_byte), LOG_PIC);
  281. }
  282. };
  283. function port21_read()
  284. {
  285. //dbg_log("21h read " + h(~this.irq_mask & 0xff), LOG_PIC);
  286. return ~this.irq_mask & 0xFF;
  287. };
  288. function port4D0_read()
  289. {
  290. dbg_log("elcr read: " + h(this.elcr, 2), LOG_PIC);
  291. return this.elcr;
  292. }
  293. function port4D0_write(value)
  294. {
  295. dbg_log("elcr write: " + h(value, 2), LOG_PIC);
  296. // set by seabios to 00 0C (only set for pci interrupts)
  297. this.elcr = value;
  298. }
  299. if(this.is_master)
  300. {
  301. this.set_irq = function(irq_number)
  302. {
  303. dbg_assert(irq_number >= 0 && irq_number < 16);
  304. if(PIC_LOG_VERBOSE)
  305. {
  306. dbg_log("master> set irq " + irq_number, LOG_PIC);
  307. }
  308. if(irq_number >= 8)
  309. {
  310. this.slave.set_irq(irq_number - 8);
  311. irq_number = 2;
  312. }
  313. var irq_mask = 1 << irq_number;
  314. if((this.irq_value & irq_mask) === 0)
  315. {
  316. this.irr |= irq_mask & ~this.irq_value;
  317. this.irq_value |= irq_mask;
  318. this.cpu.handle_irqs();
  319. }
  320. };
  321. this.clear_irq = function(irq_number)
  322. {
  323. dbg_assert(irq_number >= 0 && irq_number < 16);
  324. if(PIC_LOG_VERBOSE)
  325. {
  326. dbg_log("master> clear irq " + irq_number, LOG_PIC);
  327. }
  328. if(irq_number >= 8)
  329. {
  330. this.slave.clear_irq(irq_number - 8);
  331. irq_number = 2;
  332. }
  333. var irq_mask = 1 << irq_number;
  334. if(this.irq_value & irq_mask)
  335. {
  336. this.irq_value &= ~irq_mask
  337. this.irr &= ~irq_mask;
  338. }
  339. };
  340. }
  341. else
  342. {
  343. this.set_irq = function(irq_number)
  344. {
  345. dbg_assert(irq_number >= 0 && irq_number < 8);
  346. if(PIC_LOG_VERBOSE)
  347. {
  348. dbg_log("slave > set irq " + irq_number, LOG_PIC);
  349. }
  350. var irq_mask = 1 << irq_number;
  351. if((this.irq_value & irq_mask) === 0)
  352. {
  353. this.irr |= irq_mask;
  354. this.irq_value |= irq_mask;
  355. }
  356. };
  357. this.clear_irq = function(irq_number)
  358. {
  359. dbg_assert(irq_number >= 0 && irq_number < 8);
  360. if(PIC_LOG_VERBOSE)
  361. {
  362. dbg_log("slave > clear irq " + irq_number, LOG_PIC);
  363. }
  364. var irq_mask = 1 << irq_number;
  365. if(this.irq_value & irq_mask)
  366. {
  367. this.irq_value &= ~irq_mask;
  368. this.irr &= ~irq_mask;
  369. }
  370. };
  371. }
  372. this.get_isr = function()
  373. {
  374. return this.isr;
  375. };
  376. }
  377. PIC.prototype.get_state = function()
  378. {
  379. var state = [];
  380. state[0] = this.irq_mask;
  381. state[1] = this.irq_map;
  382. state[2] = this.isr;
  383. state[3] = this.irr;
  384. state[4] = this.is_master;
  385. state[5] = this.slave;
  386. state[6] = this.expect_icw4;
  387. state[7] = this.state;
  388. state[8] = this.read_isr;
  389. state[9] = this.auto_eoi;
  390. return state;
  391. };
  392. PIC.prototype.set_state = function(state)
  393. {
  394. this.irq_mask = state[0];
  395. this.irq_map = state[1];
  396. this.isr = state[2];
  397. this.irr = state[3];
  398. this.is_master = state[4];
  399. this.slave = state[5];
  400. this.expect_icw4 = state[6];
  401. this.state = state[7];
  402. this.read_isr = state[8];
  403. this.auto_eoi = state[9];
  404. };