ps2.js 21 KB

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