dma.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. "use strict";
  2. /**
  3. * @constructor
  4. * @param {CPU} cpu
  5. */
  6. function DMA(cpu)
  7. {
  8. /** @const @type {CPU} */
  9. this.cpu = cpu;
  10. this.channel_page = new Uint8Array(8);
  11. this.channel_pagehi = new Uint8Array(8);
  12. this.channel_addr = new Uint16Array(8);
  13. this.channel_addr_init = new Uint16Array(8);
  14. this.channel_count = new Uint16Array(8);
  15. this.channel_count_init = new Uint16Array(8);
  16. this.channel_mask = new Uint8Array(8);
  17. this.channel_mode = new Uint8Array(8);
  18. this.unmask_listeners = [];
  19. this.lsb_msb_flipflop = 0;
  20. var io = cpu.io;
  21. io.register_write(0x00, this, this.port_addr_write.bind(this, 0));
  22. io.register_write(0x02, this, this.port_addr_write.bind(this, 1));
  23. io.register_write(0x04, this, this.port_addr_write.bind(this, 2));
  24. io.register_write(0x06, this, this.port_addr_write.bind(this, 3));
  25. io.register_write(0x01, this, this.port_count_write.bind(this, 0));
  26. io.register_write(0x03, this, this.port_count_write.bind(this, 1));
  27. io.register_write(0x05, this, this.port_count_write.bind(this, 2));
  28. io.register_write(0x07, this, this.port_count_write.bind(this, 3));
  29. io.register_read(0x00, this, this.port_addr_read.bind(this, 0));
  30. io.register_read(0x02, this, this.port_addr_read.bind(this, 1));
  31. io.register_read(0x04, this, this.port_addr_read.bind(this, 2));
  32. io.register_read(0x06, this, this.port_addr_read.bind(this, 3));
  33. io.register_read(0x01, this, this.port_count_read.bind(this, 0));
  34. io.register_read(0x03, this, this.port_count_read.bind(this, 1));
  35. io.register_read(0x05, this, this.port_count_read.bind(this, 2));
  36. io.register_read(0x07, this, this.port_count_read.bind(this, 3));
  37. io.register_write(0xC0, this, this.port_addr_write.bind(this, 4));
  38. io.register_write(0xC4, this, this.port_addr_write.bind(this, 5));
  39. io.register_write(0xC8, this, this.port_addr_write.bind(this, 6));
  40. io.register_write(0xCC, this, this.port_addr_write.bind(this, 7));
  41. io.register_write(0xC2, this, this.port_count_write.bind(this, 4));
  42. io.register_write(0xC6, this, this.port_count_write.bind(this, 5));
  43. io.register_write(0xCA, this, this.port_count_write.bind(this, 6));
  44. io.register_write(0xCE, this, this.port_count_write.bind(this, 7));
  45. io.register_read(0xC0, this, this.port_addr_read.bind(this, 4));
  46. io.register_read(0xC4, this, this.port_addr_read.bind(this, 5));
  47. io.register_read(0xC8, this, this.port_addr_read.bind(this, 6));
  48. io.register_read(0xCC, this, this.port_addr_read.bind(this, 7));
  49. io.register_read(0xC2, this, this.port_count_read.bind(this, 4));
  50. io.register_read(0xC6, this, this.port_count_read.bind(this, 5));
  51. io.register_read(0xCA, this, this.port_count_read.bind(this, 6));
  52. io.register_read(0xCE, this, this.port_count_read.bind(this, 7));
  53. io.register_write(0x87, this, this.port_page_write.bind(this, 0));
  54. io.register_write(0x83, this, this.port_page_write.bind(this, 1));
  55. io.register_write(0x81, this, this.port_page_write.bind(this, 2));
  56. io.register_write(0x82, this, this.port_page_write.bind(this, 3));
  57. io.register_write(0x8F, this, this.port_page_write.bind(this, 4));
  58. io.register_write(0x8B, this, this.port_page_write.bind(this, 5));
  59. io.register_write(0x89, this, this.port_page_write.bind(this, 6));
  60. io.register_write(0x8A, this, this.port_page_write.bind(this, 7));
  61. io.register_read(0x87, this, this.port_page_read.bind(this, 0));
  62. io.register_read(0x83, this, this.port_page_read.bind(this, 1));
  63. io.register_read(0x81, this, this.port_page_read.bind(this, 2));
  64. io.register_read(0x82, this, this.port_page_read.bind(this, 3));
  65. io.register_read(0x8F, this, this.port_page_read.bind(this, 4));
  66. io.register_read(0x8B, this, this.port_page_read.bind(this, 5));
  67. io.register_read(0x89, this, this.port_page_read.bind(this, 6));
  68. io.register_read(0x8A, this, this.port_page_read.bind(this, 7));
  69. io.register_write(0x487, this, this.port_pagehi_write.bind(this, 0));
  70. io.register_write(0x483, this, this.port_pagehi_write.bind(this, 1));
  71. io.register_write(0x481, this, this.port_pagehi_write.bind(this, 2));
  72. io.register_write(0x482, this, this.port_pagehi_write.bind(this, 3));
  73. io.register_write(0x48B, this, this.port_pagehi_write.bind(this, 5));
  74. io.register_write(0x489, this, this.port_pagehi_write.bind(this, 6));
  75. io.register_write(0x48A, this, this.port_pagehi_write.bind(this, 7));
  76. io.register_read(0x487, this, this.port_pagehi_read.bind(this, 0));
  77. io.register_read(0x483, this, this.port_pagehi_read.bind(this, 1));
  78. io.register_read(0x481, this, this.port_pagehi_read.bind(this, 2));
  79. io.register_read(0x482, this, this.port_pagehi_read.bind(this, 3));
  80. io.register_read(0x48B, this, this.port_pagehi_read.bind(this, 5));
  81. io.register_read(0x489, this, this.port_pagehi_read.bind(this, 6));
  82. io.register_read(0x48A, this, this.port_pagehi_read.bind(this, 7));
  83. io.register_write(0x0A, this, this.port_singlemask_write.bind(this, 0));
  84. io.register_write(0xD4, this, this.port_singlemask_write.bind(this, 4));
  85. io.register_write(0x0F, this, this.port_multimask_write.bind(this, 0));
  86. io.register_write(0xDE, this, this.port_multimask_write.bind(this, 4));
  87. io.register_read(0x0F, this, this.port_multimask_read.bind(this, 0));
  88. io.register_read(0xDE, this, this.port_multimask_read.bind(this, 4));
  89. io.register_write(0x0B, this, this.port_mode_write.bind(this, 0));
  90. io.register_write(0xD6, this, this.port_mode_write.bind(this, 4));
  91. io.register_write(0x0C, this, this.portC_write);
  92. io.register_write(0xD8, this, this.portC_write);
  93. }
  94. DMA.prototype.get_state = function()
  95. {
  96. return [
  97. this.channel_page,
  98. this.channel_pagehi,
  99. this.channel_addr,
  100. this.channel_addr_init,
  101. this.channel_count,
  102. this.channel_count_init,
  103. this.channel_mask,
  104. this.channel_mode,
  105. this.lsb_msb_flipflop,
  106. ];
  107. };
  108. DMA.prototype.set_state = function(state)
  109. {
  110. this.channel_page = state[0];
  111. this.channel_pagehi = state[1];
  112. this.channel_addr = state[2];
  113. this.channel_addr_init = state[3];
  114. this.channel_count = state[4];
  115. this.channel_count_init = state[5];
  116. this.channel_mask = state[6];
  117. this.channel_mode = state[7];
  118. this.lsb_msb_flipflop = state[8];
  119. };
  120. DMA.prototype.port_count_write = function(channel, data_byte)
  121. {
  122. dbg_log("count write [" + channel + "] = " + h(data_byte), LOG_DMA);
  123. this.channel_count[channel] =
  124. this.flipflop_get(this.channel_count[channel], data_byte, false);
  125. this.channel_count_init[channel] =
  126. this.flipflop_get(this.channel_count_init[channel], data_byte, true);
  127. };
  128. DMA.prototype.port_count_read = function(channel)
  129. {
  130. dbg_log("count read [" + channel + "] -> " + h(this.channel_count[channel]), LOG_DMA);
  131. return this.flipflop_read(this.channel_count[channel]);
  132. };
  133. DMA.prototype.port_addr_write = function(channel, data_byte)
  134. {
  135. dbg_log("addr write [" + channel + "] = " + h(data_byte), LOG_DMA);
  136. this.channel_addr[channel] =
  137. this.flipflop_get(this.channel_addr[channel], data_byte, false);
  138. this.channel_addr_init[channel] =
  139. this.flipflop_get(this.channel_addr_init[channel], data_byte, true);
  140. };
  141. DMA.prototype.port_addr_read = function(channel)
  142. {
  143. dbg_log("addr read [" + channel + "] -> " + h(this.channel_addr[channel]), LOG_DMA);
  144. return this.flipflop_read(this.channel_addr[channel]);
  145. };
  146. DMA.prototype.port_pagehi_write = function(channel, data_byte)
  147. {
  148. dbg_log("pagehi write [" + channel + "] = " + h(data_byte), LOG_DMA);
  149. this.channel_pagehi[channel] = data_byte;
  150. };
  151. DMA.prototype.port_pagehi_read = function(channel)
  152. {
  153. dbg_log("pagehi read [" + channel + "]", LOG_DMA);
  154. return this.channel_pagehi[channel];
  155. };
  156. DMA.prototype.port_page_write = function(channel, data_byte)
  157. {
  158. dbg_log("page write [" + channel + "] = " + h(data_byte), LOG_DMA);
  159. this.channel_page[channel] = data_byte;
  160. };
  161. DMA.prototype.port_page_read = function(channel)
  162. {
  163. dbg_log("page read [" + channel + "]", LOG_DMA);
  164. return this.channel_page[channel];
  165. };
  166. DMA.prototype.port_singlemask_write = function(channel_offset, data_byte)
  167. {
  168. var channel = (data_byte & 0x3) + channel_offset;
  169. var value = data_byte & 0x4 ? 1 : 0;
  170. dbg_log("singlechannel mask write [" + channel + "] = " + value, LOG_DMA);
  171. this.update_mask(channel, value);
  172. };
  173. DMA.prototype.port_multimask_write = function(channel_offset, data_byte)
  174. {
  175. dbg_log("multichannel mask write: " + h(data_byte), LOG_DMA);
  176. for(var i = 0; i < 4; i++)
  177. {
  178. this.update_mask(channel_offset + i, data_byte & (1 << i));
  179. }
  180. };
  181. DMA.prototype.port_multimask_read = function(channel_offset)
  182. {
  183. var value = 0;
  184. value |= this.channel_mask[channel_offset + 0];
  185. value |= this.channel_mask[channel_offset + 1] << 1;
  186. value |= this.channel_mask[channel_offset + 2] << 2;
  187. value |= this.channel_mask[channel_offset + 3] << 3;
  188. dbg_log("multichannel mask read: " + h(value), LOG_DMA);
  189. return value;
  190. };
  191. DMA.prototype.port_mode_write = function(channel_offset, data_byte)
  192. {
  193. var channel = (data_byte & 0x3) + channel_offset;
  194. dbg_log("mode write [" + channel + "] = " + h(data_byte), LOG_DMA);
  195. this.channel_mode[channel] = data_byte;
  196. };
  197. DMA.prototype.portC_write = function(data_byte)
  198. {
  199. dbg_log("flipflop reset", LOG_DMA);
  200. this.lsb_msb_flipflop = 0;
  201. };
  202. DMA.prototype.on_unmask = function(fn, this_value)
  203. {
  204. this.unmask_listeners.push({
  205. fn: fn,
  206. this_value: this_value,
  207. });
  208. };
  209. DMA.prototype.update_mask = function(channel, value)
  210. {
  211. if(this.channel_mask[channel] !== value)
  212. {
  213. this.channel_mask[channel] = value;
  214. if(!value)
  215. {
  216. dbg_log("firing on_unmask(" + channel + ")", LOG_DMA);
  217. for(var i = 0; i < this.unmask_listeners.length; i++)
  218. {
  219. this.unmask_listeners[i].fn.call(
  220. this.unmask_listeners[i].this_value,
  221. channel
  222. );
  223. }
  224. }
  225. }
  226. };
  227. // read data, write to memory
  228. DMA.prototype.do_read = function(buffer, start, len, channel, fn)
  229. {
  230. var read_count = this.count_get_8bit(channel),
  231. addr = this.address_get_8bit(channel);
  232. dbg_log("DMA write channel " + channel, LOG_DMA);
  233. dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
  234. if(len < read_count)
  235. {
  236. dbg_log("DMA should read more than provided: " + h(len) + " " + h(read_count), LOG_DMA);
  237. }
  238. if(start + read_count > buffer.byteLength)
  239. {
  240. dbg_log("DMA read outside of buffer", LOG_DMA);
  241. fn(true);
  242. }
  243. else
  244. {
  245. var cpu = this.cpu;
  246. this.channel_addr[channel] += read_count;
  247. buffer.get(start, read_count, function(data)
  248. {
  249. cpu.write_blob(data, addr);
  250. fn(false);
  251. });
  252. }
  253. };
  254. // write data, read memory
  255. // start and len in bytes
  256. DMA.prototype.do_write = function(buffer, start, len, channel, fn)
  257. {
  258. var read_count = (this.channel_count[channel] + 1) & 0xFFFF,
  259. bytes_per_count = channel >= 5 ? 2 : 1,
  260. read_bytes = read_count * bytes_per_count,
  261. addr = this.address_get_8bit(channel),
  262. unfinished = false,
  263. want_more = false,
  264. autoinit = this.channel_mode[channel] & 0x10;
  265. dbg_log("DMA write channel " + channel, LOG_DMA);
  266. dbg_log("to " + h(addr) + " len " + h(read_bytes), LOG_DMA);
  267. if(len < read_bytes)
  268. {
  269. dbg_log("DMA should read more than provided", LOG_DMA);
  270. read_count = Math.floor(len / bytes_per_count);
  271. read_bytes = read_count * bytes_per_count;
  272. unfinished = true;
  273. }
  274. else if(len > read_bytes)
  275. {
  276. dbg_log("DMA attempted to read more than provided", LOG_DMA);
  277. want_more = true;
  278. }
  279. if(start + read_bytes > buffer.byteLength)
  280. {
  281. dbg_log("DMA write outside of buffer", LOG_DMA);
  282. fn(true);
  283. }
  284. else
  285. {
  286. this.channel_addr[channel] += read_count;
  287. this.channel_count[channel] -= read_count;
  288. // when complete, counter should underflow to 0xFFFF
  289. if(!unfinished && autoinit)
  290. {
  291. dbg_log("DMA autoinit", LOG_DMA);
  292. this.channel_addr[channel] = this.channel_addr_init[channel];
  293. this.channel_count[channel] = this.channel_count_init[channel];
  294. }
  295. buffer.set(start,
  296. this.cpu.mem8.subarray(addr, addr + read_bytes),
  297. () =>
  298. {
  299. if(want_more && autoinit)
  300. {
  301. dbg_log("DMA continuing from start", LOG_DMA);
  302. this.do_write(buffer, start + read_bytes, len - read_bytes, channel, fn);
  303. }
  304. else
  305. {
  306. fn(false);
  307. }
  308. }
  309. );
  310. }
  311. };
  312. DMA.prototype.address_get_8bit = function(channel)
  313. {
  314. var addr = this.channel_addr[channel];
  315. // http://wiki.osdev.org/ISA_DMA#16_bit_issues
  316. if(channel >= 5)
  317. {
  318. addr = (addr << 1);
  319. }
  320. addr &= 0xFFFF;
  321. addr |= this.channel_page[channel] << 16;
  322. addr |= this.channel_pagehi[channel] << 24;
  323. return addr;
  324. };
  325. DMA.prototype.count_get_8bit = function(channel)
  326. {
  327. var count = this.channel_count[channel] + 1;
  328. if(channel >= 5)
  329. {
  330. count *= 2;
  331. }
  332. return count;
  333. };
  334. DMA.prototype.flipflop_get = function(old_dword, new_byte, continuing)
  335. {
  336. if(!continuing)
  337. {
  338. this.lsb_msb_flipflop ^= 1;
  339. }
  340. if(this.lsb_msb_flipflop)
  341. {
  342. // low byte
  343. return old_dword & ~0xFF | new_byte;
  344. }
  345. else
  346. {
  347. // high byte
  348. return old_dword & ~0xFF00 | new_byte << 8;
  349. }
  350. };
  351. DMA.prototype.flipflop_read = function(dword)
  352. {
  353. this.lsb_msb_flipflop ^= 1;
  354. if(this.lsb_msb_flipflop)
  355. {
  356. // low byte
  357. return dword & 0xFF;
  358. }
  359. else
  360. {
  361. // high byte
  362. return (dword >> 8) & 0xFF;
  363. }
  364. };