ps2.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. "use strict";
  2. /** @const */
  3. let PS2_LOG_VERBOSE = false;
  4. /**
  5. * @constructor
  6. * @param {CPU} cpu
  7. * @param {BusConnector} bus
  8. */
  9. function PS2(cpu, bus)
  10. {
  11. /** @const @type {CPU} */
  12. this.cpu = cpu;
  13. /** @const @type {BusConnector} */
  14. this.bus = bus;
  15. /** @type {boolean} */
  16. this.enable_mouse_stream = false;
  17. /** @type {boolean} */
  18. this.use_mouse = false;
  19. /** @type {boolean} */
  20. this.have_mouse = true;
  21. /** @type {number} */
  22. this.mouse_delta_x = 0;
  23. /** @type {number} */
  24. this.mouse_delta_y = 0;
  25. /** @type {number} */
  26. this.mouse_clicks = 0;
  27. /** @type {boolean} */
  28. this.have_keyboard = true;
  29. /** @type {boolean} */
  30. this.enable_keyboard_stream = false;
  31. /** @type {boolean} */
  32. this.next_is_mouse_command = false;
  33. /** @type {boolean} */
  34. this.next_read_sample = false;
  35. /** @type {boolean} */
  36. this.next_read_led = false;
  37. /** @type {boolean} */
  38. this.next_handle_scan_code_set = false;
  39. /** @type {boolean} */
  40. this.next_read_rate = false;
  41. /** @type {boolean} */
  42. this.next_read_resolution = false;
  43. /**
  44. * @type {ByteQueue}
  45. */
  46. this.kbd_buffer = new ByteQueue(1024);
  47. this.last_port60_byte = 0;
  48. /** @type {number} */
  49. this.sample_rate = 100;
  50. /** @type {number} */
  51. this.resolution = 4;
  52. /** @type {boolean} */
  53. this.scaling2 = false;
  54. /** @type {number} */
  55. this.last_mouse_packet = -1;
  56. /**
  57. * @type {ByteQueue}
  58. */
  59. this.mouse_buffer = new ByteQueue(1024);
  60. /**
  61. * @type {boolean}
  62. * Also known as DBBOUT OBF - Output Buffer Full flag
  63. */
  64. this.next_byte_is_ready = false;
  65. /** @type {boolean} */
  66. this.next_byte_is_aux = false;
  67. this.bus.register("keyboard-code", function(code)
  68. {
  69. this.kbd_send_code(code);
  70. }, this);
  71. this.bus.register("mouse-click", function(data)
  72. {
  73. this.mouse_send_click(data[0], data[1], data[2]);
  74. }, this);
  75. this.bus.register("mouse-delta", function(data)
  76. {
  77. this.mouse_send_delta(data[0], data[1]);
  78. }, this);
  79. this.bus.register("mouse-wheel", function(data)
  80. {
  81. // TODO: Mouse Wheel
  82. // http://www.computer-engineering.org/ps2mouse/
  83. }, this);
  84. this.command_register = 1 | 4;
  85. this.read_output_register = false;
  86. this.read_command_register = false;
  87. cpu.io.register_read(0x60, this, this.port60_read);
  88. cpu.io.register_read(0x64, this, this.port64_read);
  89. cpu.io.register_write(0x60, this, this.port60_write);
  90. cpu.io.register_write(0x64, this, this.port64_write);
  91. }
  92. PS2.prototype.get_state = function()
  93. {
  94. var state = [];
  95. state[0] = this.enable_mouse_stream;
  96. state[1] = this.use_mouse;
  97. state[2] = this.have_mouse;
  98. state[3] = this.mouse_delta_x;
  99. state[4] = this.mouse_delta_y;
  100. state[5] = this.mouse_clicks;
  101. state[6] = this.have_keyboard;
  102. state[7] = this.enable_keyboard_stream;
  103. state[8] = this.next_is_mouse_command;
  104. state[9] = this.next_read_sample;
  105. state[10] = this.next_read_led;
  106. state[11] = this.next_handle_scan_code_set;
  107. state[12] = this.next_read_rate;
  108. state[13] = this.next_read_resolution;
  109. //state[14] = this.kbd_buffer;
  110. state[15] = this.last_port60_byte;
  111. state[16] = this.sample_rate;
  112. state[17] = this.resolution;
  113. state[18] = this.scaling2;
  114. //state[19] = this.mouse_buffer;
  115. state[20] = this.command_register;
  116. state[21] = this.read_output_register;
  117. state[22] = this.read_command_register;
  118. return state;
  119. };
  120. PS2.prototype.set_state = function(state)
  121. {
  122. this.enable_mouse_stream = state[0];
  123. this.use_mouse = state[1];
  124. this.have_mouse = state[2];
  125. this.mouse_delta_x = state[3];
  126. this.mouse_delta_y = state[4];
  127. this.mouse_clicks = state[5];
  128. this.have_keyboard = state[6];
  129. this.enable_keyboard_stream = state[7];
  130. this.next_is_mouse_command = state[8];
  131. this.next_read_sample = state[9];
  132. this.next_read_led = state[10];
  133. this.next_handle_scan_code_set = state[11];
  134. this.next_read_rate = state[12];
  135. this.next_read_resolution = state[13];
  136. //this.kbd_buffer = state[14];
  137. this.last_port60_byte = state[15];
  138. this.sample_rate = state[16];
  139. this.resolution = state[17];
  140. this.scaling2 = state[18];
  141. //this.mouse_buffer = state[19];
  142. this.command_register = state[20];
  143. this.read_output_register = state[21];
  144. this.read_command_register = state[22];
  145. this.next_byte_is_ready = false;
  146. this.next_byte_is_aux = false;
  147. this.kbd_buffer.clear();
  148. this.mouse_buffer.clear();
  149. this.bus.send("mouse-enable", this.use_mouse);
  150. };
  151. PS2.prototype.raise_irq = function()
  152. {
  153. if(this.next_byte_is_ready)
  154. {
  155. // Wait until previous byte is read
  156. // http://halicery.com/Hardware/8042/8042_1503033_TXT.htm
  157. return;
  158. }
  159. // Kbd has priority over aux
  160. if(this.kbd_buffer.length)
  161. {
  162. this.kbd_irq();
  163. }
  164. else if(this.mouse_buffer.length)
  165. {
  166. this.mouse_irq();
  167. }
  168. };
  169. PS2.prototype.mouse_irq = function()
  170. {
  171. this.next_byte_is_ready = true;
  172. this.next_byte_is_aux = true;
  173. if(this.command_register & 2)
  174. {
  175. dbg_log("Mouse irq", LOG_PS2);
  176. // Pulse the irq line
  177. // Note: can't lower immediately after rising, so lower before rising
  178. // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
  179. this.cpu.device_lower_irq(12);
  180. this.cpu.device_raise_irq(12);
  181. }
  182. };
  183. PS2.prototype.kbd_irq = function()
  184. {
  185. this.next_byte_is_ready = true;
  186. this.next_byte_is_aux = false;
  187. if(this.command_register & 1)
  188. {
  189. dbg_log("Keyboard irq", LOG_PS2);
  190. // Pulse the irq line
  191. // Note: can't lower immediately after rising, so lower before rising
  192. // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
  193. this.cpu.device_lower_irq(1);
  194. this.cpu.device_raise_irq(1);
  195. }
  196. };
  197. PS2.prototype.kbd_send_code = function(code)
  198. {
  199. if(this.enable_keyboard_stream)
  200. {
  201. dbg_log("adding kbd code: " + h(code), LOG_PS2);
  202. this.kbd_buffer.push(code);
  203. this.raise_irq();
  204. }
  205. };
  206. PS2.prototype.mouse_send_delta = function(delta_x, delta_y)
  207. {
  208. if(!this.have_mouse || !this.use_mouse)
  209. {
  210. return;
  211. }
  212. // note: delta_x or delta_y can be floating point numbers
  213. var factor = this.resolution * this.sample_rate / 80;
  214. this.mouse_delta_x += delta_x * factor;
  215. this.mouse_delta_y += delta_y * factor;
  216. if(this.enable_mouse_stream)
  217. {
  218. var change_x = this.mouse_delta_x | 0,
  219. change_y = this.mouse_delta_y | 0;
  220. if(change_x || change_y)
  221. {
  222. var now = Date.now();
  223. //if(now - this.last_mouse_packet < 1000 / this.sample_rate)
  224. //{
  225. // // TODO: set timeout
  226. // return;
  227. //}
  228. this.mouse_delta_x -= change_x;
  229. this.mouse_delta_y -= change_y;
  230. this.send_mouse_packet(change_x, change_y);
  231. }
  232. }
  233. };
  234. PS2.prototype.mouse_send_click = function(left, middle, right)
  235. {
  236. if(!this.have_mouse || !this.use_mouse)
  237. {
  238. return;
  239. }
  240. this.mouse_clicks = left | right << 1 | middle << 2;
  241. if(this.enable_mouse_stream)
  242. {
  243. this.send_mouse_packet(0, 0);
  244. }
  245. };
  246. PS2.prototype.send_mouse_packet = function(dx, dy)
  247. {
  248. var info_byte =
  249. (dy < 0) << 5 |
  250. (dx < 0) << 4 |
  251. 1 << 3 |
  252. this.mouse_clicks,
  253. delta_x = dx,
  254. delta_y = dy;
  255. this.last_mouse_packet = Date.now();
  256. //if(this.scaling2)
  257. //{
  258. // // only in automatic packets, not 0xEB requests
  259. // delta_x = this.apply_scaling2(delta_x);
  260. // delta_y = this.apply_scaling2(delta_y);
  261. //}
  262. this.mouse_buffer.push(info_byte);
  263. this.mouse_buffer.push(delta_x);
  264. this.mouse_buffer.push(delta_y);
  265. if(PS2_LOG_VERBOSE)
  266. {
  267. dbg_log("adding mouse packets: " + [info_byte, dx, dy], LOG_PS2);
  268. }
  269. this.raise_irq();
  270. };
  271. PS2.prototype.apply_scaling2 = function(n)
  272. {
  273. // http://www.computer-engineering.org/ps2mouse/#Inputs.2C_Resolution.2C_and_Scaling
  274. var abs = Math.abs(n),
  275. sign = n >> 31;
  276. switch(abs)
  277. {
  278. case 0:
  279. case 1:
  280. case 3:
  281. return n;
  282. case 2:
  283. return sign;
  284. case 4:
  285. return 6 * sign;
  286. case 5:
  287. return 9 * sign;
  288. default:
  289. return n << 1;
  290. }
  291. };
  292. PS2.prototype.port60_read = function()
  293. {
  294. //dbg_log("port 60 read: " + (buffer[0] || "(none)"));
  295. this.next_byte_is_ready = false;
  296. if(!this.kbd_buffer.length && !this.mouse_buffer.length)
  297. {
  298. // should not happen
  299. dbg_log("Port 60 read: Empty", LOG_PS2);
  300. return this.last_port60_byte;
  301. }
  302. if(this.next_byte_is_aux)
  303. {
  304. this.cpu.device_lower_irq(12);
  305. this.last_port60_byte = this.mouse_buffer.shift();
  306. dbg_log("Port 60 read (mouse): " + h(this.last_port60_byte), LOG_PS2);
  307. }
  308. else
  309. {
  310. this.cpu.device_lower_irq(1);
  311. this.last_port60_byte = this.kbd_buffer.shift();
  312. dbg_log("Port 60 read (kbd) : " + h(this.last_port60_byte), LOG_PS2);
  313. }
  314. if(this.kbd_buffer.length || this.mouse_buffer.length)
  315. {
  316. this.raise_irq();
  317. }
  318. return this.last_port60_byte;
  319. };
  320. PS2.prototype.port64_read = function()
  321. {
  322. // status port
  323. var status_byte = 0x10;
  324. if(this.next_byte_is_ready)
  325. {
  326. status_byte |= 0x1;
  327. }
  328. if(this.next_byte_is_aux)
  329. {
  330. status_byte |= 0x20;
  331. }
  332. dbg_log("port 64 read: " + h(status_byte), LOG_PS2);
  333. return status_byte;
  334. };
  335. PS2.prototype.port60_write = function(write_byte)
  336. {
  337. dbg_log("port 60 write: " + h(write_byte), LOG_PS2);
  338. if(this.read_command_register)
  339. {
  340. this.command_register = write_byte;
  341. this.read_command_register = false;
  342. // not sure, causes "spurious ack" in Linux
  343. //this.kbd_buffer.push(0xFA);
  344. //this.kbd_irq();
  345. dbg_log("Keyboard command register = " + h(this.command_register), LOG_PS2);
  346. }
  347. else if(this.read_output_register)
  348. {
  349. this.read_output_register = false;
  350. this.mouse_buffer.clear();
  351. this.mouse_buffer.push(write_byte);
  352. this.mouse_irq();
  353. }
  354. else if(this.next_read_sample)
  355. {
  356. this.next_read_sample = false;
  357. this.mouse_buffer.clear();
  358. this.mouse_buffer.push(0xFA);
  359. this.sample_rate = write_byte;
  360. dbg_log("mouse sample rate: " + h(write_byte), LOG_PS2);
  361. if(!this.sample_rate)
  362. {
  363. dbg_log("invalid sample rate, reset to 100", LOG_PS2);
  364. this.sample_rate = 100;
  365. }
  366. this.mouse_irq();
  367. }
  368. else if(this.next_read_resolution)
  369. {
  370. this.next_read_resolution = false;
  371. this.mouse_buffer.clear();
  372. this.mouse_buffer.push(0xFA);
  373. if(write_byte > 3)
  374. {
  375. this.resolution = 4;
  376. dbg_log("invalid resolution, resetting to 4", LOG_PS2);
  377. }
  378. else
  379. {
  380. this.resolution = 1 << write_byte;
  381. dbg_log("resolution: " + this.resolution, LOG_PS2);
  382. }
  383. this.mouse_irq();
  384. }
  385. else if(this.next_read_led)
  386. {
  387. // nope
  388. this.next_read_led = false;
  389. this.kbd_buffer.push(0xFA);
  390. this.kbd_irq();
  391. }
  392. else if(this.next_handle_scan_code_set)
  393. {
  394. this.next_handle_scan_code_set = false;
  395. this.kbd_buffer.push(0xFA);
  396. this.kbd_irq();
  397. if(write_byte)
  398. {
  399. // set scan code set
  400. }
  401. else
  402. {
  403. this.kbd_buffer.push(2);
  404. }
  405. }
  406. else if(this.next_read_rate)
  407. {
  408. // nope
  409. this.next_read_rate = false;
  410. this.kbd_buffer.push(0xFA);
  411. this.kbd_irq();
  412. }
  413. else if(this.next_is_mouse_command)
  414. {
  415. this.next_is_mouse_command = false;
  416. dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
  417. if(!this.have_mouse)
  418. {
  419. return;
  420. }
  421. // send ack
  422. this.kbd_buffer.clear();
  423. this.mouse_buffer.clear();
  424. this.mouse_buffer.push(0xFA);
  425. switch(write_byte)
  426. {
  427. case 0xE6:
  428. // set scaling to 1:1
  429. dbg_log("Scaling 1:1", LOG_PS2);
  430. this.scaling2 = false;
  431. break;
  432. case 0xE7:
  433. // set scaling to 2:1
  434. dbg_log("Scaling 2:1", LOG_PS2);
  435. this.scaling2 = true;
  436. break;
  437. case 0xE8:
  438. // set mouse resolution
  439. this.next_read_resolution = true;
  440. break;
  441. case 0xE9:
  442. // status request - send one packet
  443. this.send_mouse_packet(0, 0);
  444. break;
  445. case 0xEB:
  446. // request single packet
  447. dbg_log("unimplemented request single packet", LOG_PS2);
  448. this.send_mouse_packet(0, 0);
  449. break;
  450. case 0xF2:
  451. // MouseID Byte
  452. this.mouse_buffer.push(0);
  453. this.mouse_buffer.push(0);
  454. this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
  455. break;
  456. case 0xF3:
  457. // sample rate
  458. this.next_read_sample = true;
  459. break;
  460. case 0xF4:
  461. // enable streaming
  462. this.enable_mouse_stream = true;
  463. this.use_mouse = true;
  464. this.bus.send("mouse-enable", true);
  465. this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
  466. break;
  467. case 0xF5:
  468. // disable streaming
  469. this.enable_mouse_stream = false;
  470. break;
  471. case 0xF6:
  472. // set defaults
  473. this.enable_mouse_stream = false;
  474. this.sample_rate = 100;
  475. this.scaling2 = false;
  476. this.resolution = 4;
  477. break;
  478. case 0xFF:
  479. // reset, send completion code
  480. dbg_log("Mouse reset", LOG_PS2);
  481. this.mouse_buffer.push(0xAA);
  482. this.mouse_buffer.push(0);
  483. this.use_mouse = true;
  484. this.bus.send("mouse-enable", true);
  485. this.enable_mouse_stream = false;
  486. this.sample_rate = 100;
  487. this.scaling2 = false;
  488. this.resolution = 4;
  489. this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
  490. break;
  491. default:
  492. dbg_log("Unimplemented mouse command: " + h(write_byte), LOG_PS2);
  493. }
  494. this.mouse_irq();
  495. }
  496. else
  497. {
  498. dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
  499. // send ack
  500. this.mouse_buffer.clear();
  501. this.kbd_buffer.clear();
  502. this.kbd_buffer.push(0xFA);
  503. switch(write_byte)
  504. {
  505. case 0xED:
  506. this.next_read_led = true;
  507. break;
  508. case 0xF0:
  509. // get/set scan code set
  510. this.next_handle_scan_code_set = true;
  511. break;
  512. case 0xF2:
  513. // identify
  514. this.kbd_buffer.push(0xAB);
  515. this.kbd_buffer.push(83);
  516. break;
  517. case 0xF3:
  518. // Set typematic rate and delay
  519. this.next_read_rate = true;
  520. break;
  521. case 0xF4:
  522. // enable scanning
  523. dbg_log("kbd enable scanning", LOG_PS2);
  524. this.enable_keyboard_stream = true;
  525. break;
  526. case 0xF5:
  527. // disable scanning
  528. dbg_log("kbd disable scanning", LOG_PS2);
  529. this.enable_keyboard_stream = false;
  530. break;
  531. case 0xF6:
  532. // reset defaults
  533. //this.enable_keyboard_stream = false;
  534. break;
  535. case 0xFF:
  536. this.kbd_buffer.clear();
  537. this.kbd_buffer.push(0xFA);
  538. this.kbd_buffer.push(0xAA);
  539. this.kbd_buffer.push(0);
  540. break;
  541. default:
  542. dbg_log("Unimplemented keyboard command: " + h(write_byte), LOG_PS2);
  543. }
  544. this.kbd_irq();
  545. }
  546. };
  547. PS2.prototype.port64_write = function(write_byte)
  548. {
  549. dbg_log("port 64 write: " + h(write_byte), LOG_PS2);
  550. switch(write_byte)
  551. {
  552. case 0x20:
  553. this.kbd_buffer.clear();
  554. this.mouse_buffer.clear();
  555. this.kbd_buffer.push(this.command_register);
  556. this.kbd_irq();
  557. break;
  558. case 0x60:
  559. this.read_command_register = true;
  560. break;
  561. case 0xD3:
  562. this.read_output_register = true;
  563. break;
  564. case 0xD4:
  565. this.next_is_mouse_command = true;
  566. break;
  567. case 0xA7:
  568. // Disable second port
  569. dbg_log("Disable second port", LOG_PS2);
  570. this.command_register |= 0x20;
  571. break;
  572. case 0xA8:
  573. // Enable second port
  574. dbg_log("Enable second port", LOG_PS2);
  575. this.command_register &= ~0x20;
  576. break;
  577. case 0xA9:
  578. // test second ps/2 port
  579. this.kbd_buffer.clear();
  580. this.mouse_buffer.clear();
  581. this.kbd_buffer.push(0);
  582. this.kbd_irq();
  583. break;
  584. case 0xAA:
  585. this.kbd_buffer.clear();
  586. this.mouse_buffer.clear();
  587. this.kbd_buffer.push(0x55);
  588. this.kbd_irq();
  589. break;
  590. case 0xAB:
  591. // Test first PS/2 port
  592. this.kbd_buffer.clear();
  593. this.mouse_buffer.clear();
  594. this.kbd_buffer.push(0);
  595. this.kbd_irq();
  596. break;
  597. case 0xAD:
  598. // Disable Keyboard
  599. dbg_log("Disable Keyboard", LOG_PS2);
  600. this.command_register |= 0x10;
  601. break;
  602. case 0xAE:
  603. // Enable Keyboard
  604. dbg_log("Enable Keyboard", LOG_PS2);
  605. this.command_register &= ~0x10;
  606. break;
  607. case 0xFE:
  608. dbg_log("CPU reboot via PS2");
  609. this.cpu.reboot_internal();
  610. break;
  611. default:
  612. dbg_log("port 64: Unimplemented command byte: " + h(write_byte), LOG_PS2);
  613. }
  614. };