io.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. "use strict";
  2. /**
  3. * The ISA IO bus
  4. * Devices register their ports here
  5. *
  6. * @constructor
  7. * @param {CPU} cpu
  8. */
  9. function IO(cpu)
  10. {
  11. /** @const */
  12. this.ports = [];
  13. /** @const @type {CPU} */
  14. this.cpu = cpu;
  15. for(var i = 0; i < 0x10000; i++)
  16. {
  17. this.ports[i] = this.create_empty_entry();
  18. }
  19. var memory_size = cpu.memory_size[0];
  20. for(var i = 0; (i << MMAP_BLOCK_BITS) < memory_size; i++)
  21. {
  22. // avoid sparse arrays
  23. cpu.memory_map_read8[i] = cpu.memory_map_write8[i] = undefined;
  24. cpu.memory_map_read32[i] = cpu.memory_map_write32[i] = undefined;
  25. }
  26. this.mmap_register(memory_size, MMAP_MAX - memory_size,
  27. function(addr) {
  28. // read outside of the memory size
  29. dbg_log("Read from unmapped memory space, addr=" + h(addr >>> 0, 8), LOG_IO);
  30. return 0xFF;
  31. },
  32. function(addr, value) {
  33. // write outside of the memory size
  34. dbg_log("Write to unmapped memory space, addr=" + h(addr >>> 0, 8) + " value=" + h(value, 2), LOG_IO);
  35. },
  36. function(addr) {
  37. dbg_log("Read from unmapped memory space, addr=" + h(addr >>> 0, 8), LOG_IO);
  38. return -1;
  39. },
  40. function(addr, value) {
  41. dbg_log("Write to unmapped memory space, addr=" + h(addr >>> 0, 8) + " value=" + h(value >>> 0, 8), LOG_IO);
  42. }
  43. );
  44. }
  45. IO.prototype.create_empty_entry = function()
  46. {
  47. return {
  48. read8: this.empty_port_read8,
  49. read16: this.empty_port_read16,
  50. read32: this.empty_port_read32,
  51. write8: this.empty_port_write,
  52. write16: this.empty_port_write,
  53. write32: this.empty_port_write,
  54. device: undefined,
  55. };
  56. };
  57. IO.prototype.empty_port_read8 = function()
  58. {
  59. return 0xFF;
  60. };
  61. IO.prototype.empty_port_read16 = function()
  62. {
  63. return 0xFFFF;
  64. };
  65. IO.prototype.empty_port_read32 = function()
  66. {
  67. return -1;
  68. };
  69. IO.prototype.empty_port_write = function(x)
  70. {
  71. };
  72. /**
  73. * @param {number} port_addr
  74. * @param {Object} device
  75. * @param {function():number=} r8
  76. * @param {function():number=} r16
  77. * @param {function():number=} r32
  78. */
  79. IO.prototype.register_read = function(port_addr, device, r8, r16, r32)
  80. {
  81. dbg_assert(typeof port_addr === "number");
  82. dbg_assert(typeof device === "object");
  83. dbg_assert(!r8 || typeof r8 === "function");
  84. dbg_assert(!r16 || typeof r16 === "function");
  85. dbg_assert(!r32 || typeof r32 === "function");
  86. dbg_assert(r8 || r16 || r32);
  87. if(DEBUG)
  88. {
  89. var fail = function(n) {
  90. dbg_assert(false, "Overlapped read" + n + " " + h(port_addr, 4) + " (" + device.name + ")");
  91. return -1 >>> (32 - n) | 0;
  92. };
  93. if(!r8) r8 = fail.bind(this, 8);
  94. if(!r16) r16 = fail.bind(this, 16);
  95. if(!r32) r32 = fail.bind(this, 32);
  96. }
  97. if(r8) this.ports[port_addr].read8 = r8;
  98. if(r16) this.ports[port_addr].read16 = r16;
  99. if(r32) this.ports[port_addr].read32 = r32;
  100. this.ports[port_addr].device = device;
  101. };
  102. /**
  103. * @param {number} port_addr
  104. * @param {Object} device
  105. * @param {function(number)=} w8
  106. * @param {function(number)=} w16
  107. * @param {function(number)=} w32
  108. */
  109. IO.prototype.register_write = function(port_addr, device, w8, w16, w32)
  110. {
  111. dbg_assert(typeof port_addr === "number");
  112. dbg_assert(typeof device === "object");
  113. dbg_assert(!w8 || typeof w8 === "function");
  114. dbg_assert(!w16 || typeof w16 === "function");
  115. dbg_assert(!w32 || typeof w32 === "function");
  116. dbg_assert(w8 || w16 || w32);
  117. if(DEBUG)
  118. {
  119. var fail = function(n) {
  120. dbg_assert(false, "Overlapped write" + n + " " + h(port_addr) + " (" + device.name + ")");
  121. };
  122. if(!w8) w8 = fail.bind(this, 8);
  123. if(!w16) w16 = fail.bind(this, 16);
  124. if(!w32) w32 = fail.bind(this, 32);
  125. }
  126. if(w8) this.ports[port_addr].write8 = w8;
  127. if(w16) this.ports[port_addr].write16 = w16;
  128. if(w32) this.ports[port_addr].write32 = w32;
  129. this.ports[port_addr].device = device;
  130. };
  131. /**
  132. * > Any two consecutive 8-bit ports can be treated as a 16-bit port;
  133. * > and four consecutive 8-bit ports can be treated as a 32-bit port
  134. * > http://css.csail.mit.edu/6.858/2012/readings/i386/s08_01.htm
  135. *
  136. * This info is not correct for all ports, but handled by the following functions
  137. *
  138. * Register the write of 2 or 4 consecutive 8-bit ports, 1 or 2 16-bit
  139. * ports and 0 or 1 32-bit ports
  140. *
  141. * @param {number} port_addr
  142. * @param {!Object} device
  143. * @param {function():number} r8_1
  144. * @param {function():number} r8_2
  145. * @param {function():number=} r8_3
  146. * @param {function():number=} r8_4
  147. */
  148. IO.prototype.register_read_consecutive = function(port_addr, device, r8_1, r8_2, r8_3, r8_4)
  149. {
  150. dbg_assert(arguments.length === 4 || arguments.length === 6);
  151. function r16_1()
  152. {
  153. return r8_1.call(this) |
  154. r8_2.call(this) << 8;
  155. }
  156. function r16_2()
  157. {
  158. return r8_3.call(this) |
  159. r8_4.call(this) << 8;
  160. }
  161. function r32()
  162. {
  163. return r8_1.call(this) |
  164. r8_2.call(this) << 8 |
  165. r8_3.call(this) << 16 |
  166. r8_4.call(this) << 24;
  167. }
  168. if(r8_3 && r8_4)
  169. {
  170. this.register_read(port_addr, device, r8_1, r16_1, r32);
  171. this.register_read(port_addr + 1, device, r8_2);
  172. this.register_read(port_addr + 2, device, r8_3, r16_2);
  173. this.register_read(port_addr + 3, device, r8_4);
  174. }
  175. else
  176. {
  177. this.register_read(port_addr, device, r8_1, r16_1);
  178. this.register_read(port_addr + 1, device, r8_2);
  179. }
  180. };
  181. /**
  182. * @param {number} port_addr
  183. * @param {!Object} device
  184. * @param {function(number)} w8_1
  185. * @param {function(number)} w8_2
  186. * @param {function(number)=} w8_3
  187. * @param {function(number)=} w8_4
  188. */
  189. IO.prototype.register_write_consecutive = function(port_addr, device, w8_1, w8_2, w8_3, w8_4)
  190. {
  191. dbg_assert(arguments.length === 4 || arguments.length === 6);
  192. function w16_1(data)
  193. {
  194. w8_1.call(this, data & 0xFF);
  195. w8_2.call(this, data >> 8 & 0xFF);
  196. }
  197. function w16_2(data)
  198. {
  199. w8_3.call(this, data & 0xFF);
  200. w8_4.call(this, data >> 8 & 0xFF);
  201. }
  202. function w32(data)
  203. {
  204. w8_1.call(this, data & 0xFF);
  205. w8_2.call(this, data >> 8 & 0xFF);
  206. w8_3.call(this, data >> 16 & 0xFF);
  207. w8_4.call(this, data >>> 24);
  208. }
  209. if(w8_3 && w8_4)
  210. {
  211. this.register_write(port_addr, device, w8_1, w16_1, w32);
  212. this.register_write(port_addr + 1, device, w8_2);
  213. this.register_write(port_addr + 2, device, w8_3, w16_2);
  214. this.register_write(port_addr + 3, device, w8_4);
  215. }
  216. else
  217. {
  218. this.register_write(port_addr, device, w8_1, w16_1);
  219. this.register_write(port_addr + 1, device, w8_2);
  220. }
  221. };
  222. IO.prototype.mmap_read32_shim = function(addr)
  223. {
  224. var aligned_addr = addr >>> MMAP_BLOCK_BITS;
  225. var fn = this.cpu.memory_map_read8[aligned_addr];
  226. return fn(addr) | fn(addr + 1) << 8 |
  227. fn(addr + 2) << 16 | fn(addr + 3) << 24;
  228. };
  229. IO.prototype.mmap_write32_shim = function(addr, value)
  230. {
  231. var aligned_addr = addr >>> MMAP_BLOCK_BITS;
  232. var fn = this.cpu.memory_map_write8[aligned_addr];
  233. fn(addr, value & 0xFF);
  234. fn(addr + 1, value >> 8 & 0xFF);
  235. fn(addr + 2, value >> 16 & 0xFF);
  236. fn(addr + 3, value >>> 24);
  237. };
  238. /**
  239. * @param {number} addr
  240. * @param {number} size
  241. * @param {*} read_func8
  242. * @param {*} write_func8
  243. * @param {*=} read_func32
  244. * @param {*=} write_func32
  245. */
  246. IO.prototype.mmap_register = function(addr, size, read_func8, write_func8, read_func32, write_func32)
  247. {
  248. dbg_log("mmap_register addr=" + h(addr >>> 0, 8) + " size=" + h(size, 8), LOG_IO);
  249. dbg_assert((addr & MMAP_BLOCK_SIZE - 1) === 0);
  250. dbg_assert(size && (size & MMAP_BLOCK_SIZE - 1) === 0);
  251. if(!read_func32)
  252. read_func32 = this.mmap_read32_shim.bind(this);
  253. if(!write_func32)
  254. write_func32 = this.mmap_write32_shim.bind(this);
  255. var aligned_addr = addr >>> MMAP_BLOCK_BITS;
  256. for(; size > 0; aligned_addr++)
  257. {
  258. this.cpu.memory_map_read8[aligned_addr] = read_func8;
  259. this.cpu.memory_map_write8[aligned_addr] = write_func8;
  260. this.cpu.memory_map_read32[aligned_addr] = read_func32;
  261. this.cpu.memory_map_write32[aligned_addr] = write_func32;
  262. size -= MMAP_BLOCK_SIZE;
  263. }
  264. };
  265. IO.prototype.port_write8 = function(port_addr, data)
  266. {
  267. var entry = this.ports[port_addr];
  268. if(entry.write8 === this.empty_port_write || LOG_ALL_IO)
  269. {
  270. dbg_log(
  271. "write8 port #" + h(port_addr, 4) + " <- " + h(data, 2) + this.get_port_description(port_addr),
  272. LOG_IO
  273. );
  274. }
  275. return entry.write8.call(entry.device, data);
  276. };
  277. IO.prototype.port_write16 = function(port_addr, data)
  278. {
  279. var entry = this.ports[port_addr];
  280. if(entry.write16 === this.empty_port_write || LOG_ALL_IO)
  281. {
  282. dbg_log(
  283. "write16 port #" + h(port_addr, 4) + " <- " + h(data, 4) + this.get_port_description(port_addr),
  284. LOG_IO
  285. );
  286. }
  287. return entry.write16.call(entry.device, data);
  288. };
  289. IO.prototype.port_write32 = function(port_addr, data)
  290. {
  291. var entry = this.ports[port_addr];
  292. if(entry.write32 === this.empty_port_write || LOG_ALL_IO)
  293. {
  294. dbg_log(
  295. "write32 port #" + h(port_addr, 4) + " <- " + h(data >>> 0, 8) + this.get_port_description(port_addr),
  296. LOG_IO
  297. );
  298. }
  299. return entry.write32.call(entry.device, data);
  300. };
  301. IO.prototype.port_read8 = function(port_addr)
  302. {
  303. var entry = this.ports[port_addr];
  304. if(entry.read8 === this.empty_port_read8 || LOG_ALL_IO)
  305. {
  306. dbg_log(
  307. "read8 port #" + h(port_addr, 4) + this.get_port_description(port_addr),
  308. LOG_IO
  309. );
  310. }
  311. var value = entry.read8.call(entry.device);
  312. dbg_assert(typeof value === "number");
  313. dbg_assert(value < 0x100 && value >= 0, "8 bit port returned large value: " + h(port_addr));
  314. return value;
  315. };
  316. IO.prototype.port_read16 = function(port_addr)
  317. {
  318. var entry = this.ports[port_addr];
  319. if(entry.read16 === this.empty_port_read16 || LOG_ALL_IO)
  320. {
  321. dbg_log(
  322. "read16 port #" + h(port_addr, 4) + this.get_port_description(port_addr),
  323. LOG_IO
  324. );
  325. }
  326. var value = entry.read16.call(entry.device);
  327. dbg_assert(typeof value === "number");
  328. dbg_assert(value < 0x10000 && value >= 0, "16 bit port returned large value: " + h(port_addr));
  329. return value;
  330. };
  331. IO.prototype.port_read32 = function(port_addr)
  332. {
  333. var entry = this.ports[port_addr];
  334. if(entry.read32 === this.empty_port_read32 || LOG_ALL_IO)
  335. {
  336. dbg_log(
  337. "read32 port #" + h(port_addr, 4) + this.get_port_description(port_addr),
  338. LOG_IO
  339. );
  340. }
  341. var value = entry.read32.call(entry.device);
  342. dbg_assert((value | 0) === value);
  343. return value;
  344. };
  345. // via seabios ioport.h
  346. var debug_port_list = {
  347. 0x0004: "PORT_DMA_ADDR_2",
  348. 0x0005: "PORT_DMA_CNT_2",
  349. 0x000a: "PORT_DMA1_MASK_REG",
  350. 0x000b: "PORT_DMA1_MODE_REG",
  351. 0x000c: "PORT_DMA1_CLEAR_FF_REG",
  352. 0x000d: "PORT_DMA1_MASTER_CLEAR",
  353. 0x0020: "PORT_PIC1_CMD",
  354. 0x0021: "PORT_PIC1_DATA",
  355. 0x0040: "PORT_PIT_COUNTER0",
  356. 0x0041: "PORT_PIT_COUNTER1",
  357. 0x0042: "PORT_PIT_COUNTER2",
  358. 0x0043: "PORT_PIT_MODE",
  359. 0x0060: "PORT_PS2_DATA",
  360. 0x0061: "PORT_PS2_CTRLB",
  361. 0x0064: "PORT_PS2_STATUS",
  362. 0x0070: "PORT_CMOS_INDEX",
  363. 0x0071: "PORT_CMOS_DATA",
  364. 0x0080: "PORT_DIAG",
  365. 0x0081: "PORT_DMA_PAGE_2",
  366. 0x0092: "PORT_A20",
  367. 0x00a0: "PORT_PIC2_CMD",
  368. 0x00a1: "PORT_PIC2_DATA",
  369. 0x00b2: "PORT_SMI_CMD",
  370. 0x00b3: "PORT_SMI_STATUS",
  371. 0x00d4: "PORT_DMA2_MASK_REG",
  372. 0x00d6: "PORT_DMA2_MODE_REG",
  373. 0x00da: "PORT_DMA2_MASTER_CLEAR",
  374. 0x00f0: "PORT_MATH_CLEAR",
  375. 0x0170: "PORT_ATA2_CMD_BASE",
  376. 0x01f0: "PORT_ATA1_CMD_BASE",
  377. 0x0278: "PORT_LPT2",
  378. 0x02e8: "PORT_SERIAL4",
  379. 0x02f8: "PORT_SERIAL2",
  380. 0x0374: "PORT_ATA2_CTRL_BASE",
  381. 0x0378: "PORT_LPT1",
  382. 0x03e8: "PORT_SERIAL3",
  383. //0x03f4: "PORT_ATA1_CTRL_BASE",
  384. 0x03f0: "PORT_FD_BASE",
  385. 0x03f2: "PORT_FD_DOR",
  386. 0x03f4: "PORT_FD_STATUS",
  387. 0x03f5: "PORT_FD_DATA",
  388. 0x03f6: "PORT_HD_DATA",
  389. 0x03f7: "PORT_FD_DIR",
  390. 0x03f8: "PORT_SERIAL1",
  391. 0x0cf8: "PORT_PCI_CMD",
  392. 0x0cf9: "PORT_PCI_REBOOT",
  393. 0x0cfc: "PORT_PCI_DATA",
  394. 0x0402: "PORT_BIOS_DEBUG",
  395. 0x0510: "PORT_QEMU_CFG_CTL",
  396. 0x0511: "PORT_QEMU_CFG_DATA",
  397. 0xb000: "PORT_ACPI_PM_BASE",
  398. 0xb100: "PORT_SMB_BASE",
  399. 0x8900: "PORT_BIOS_APM"
  400. };
  401. IO.prototype.get_port_description = function(addr)
  402. {
  403. if(debug_port_list[addr])
  404. {
  405. return " (" + debug_port_list[addr] + ")";
  406. }
  407. else
  408. {
  409. return "";
  410. }
  411. };