hpet.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. "use strict";
  2. var HPET_ADDR = 0xFED00000,
  3. HPET_PERIOD = 0x05F5E100, // in nano seconds
  4. HPET_FREQ_MS = 1e12 / HPET_PERIOD, // in kHZ
  5. HPET_SUPPORT_64 = 0,
  6. HPET_COUNTER_CONFIG = 1 << 4 | HPET_SUPPORT_64 << 5,
  7. HPET_COUNTER_CONFIG_MASK = 1 << 4 | 1 << 5 | 1 << 15,
  8. HPET_NUM_COUNTERS = 4;
  9. /**
  10. * HPET - High Precision Event Timer
  11. * http://wiki.osdev.org/HPET
  12. *
  13. * @constructor
  14. * @param {CPU} cpu
  15. */
  16. function HPET(cpu)
  17. {
  18. var me = this,
  19. hpet_enabled = false,
  20. hpet_start = Date.now(),
  21. hpet_offset_low = 0,
  22. hpet_offset_high = 0,
  23. counter_read_acc_next = false,
  24. interrupt_status = 0,
  25. counter_config = new Int32Array(HPET_NUM_COUNTERS << 1),
  26. counter_comparator = new Int32Array(HPET_NUM_COUNTERS << 1),
  27. counter_accumulator = new Int32Array(HPET_NUM_COUNTERS << 1);
  28. //var counter_last_irq = new Int32Array(HPET_NUM_COUNTERS << 1);
  29. var last_check = 0;
  30. this.legacy_mode = false;
  31. this.timer = function(now)
  32. {
  33. if(!hpet_enabled)
  34. {
  35. return 100;
  36. }
  37. var
  38. counter_value = get_counter() >>> 0,
  39. config,
  40. //last_irq,
  41. comparator,
  42. do_irq;
  43. for(var i = 0; i < HPET_NUM_COUNTERS; i++)
  44. {
  45. config = counter_config[i << 1];
  46. //last_irq = counter_last_irq[i << 1] >>> 0;
  47. comparator = counter_comparator[i << 1] >>> 0;
  48. if(last_check <= counter_value ?
  49. comparator > last_check && comparator <= counter_value :
  50. comparator > last_check || comparator <= counter_value
  51. ) {
  52. do_irq = config & 4;
  53. //counter_last_irq[i << 1] = comparator;
  54. if(config & 2)
  55. {
  56. // level triggered
  57. do_irq = do_irq && !(interrupt_status & 1 << i);
  58. interrupt_status |= 1 << i;
  59. }
  60. else
  61. {
  62. // edge-triggered
  63. interrupt_status &= ~(1 << i);
  64. }
  65. if(config & 1 << 3)
  66. {
  67. // periodic mode
  68. counter_comparator[i << 1] += counter_accumulator[i << 1];
  69. }
  70. //dbg_log("do_irq=" + do_irq, LOG_HPET);
  71. if(do_irq)
  72. {
  73. if(me.legacy_mode && i === 0)
  74. {
  75. cpu.device_raise_irq(0);
  76. }
  77. else if(me.legacy_mode && i === 1)
  78. {
  79. cpu.device_raise_irq(0);
  80. }
  81. else
  82. {
  83. // TODO
  84. cpu.device_raise_irq(0);
  85. }
  86. }
  87. }
  88. }
  89. last_check = counter_value;
  90. return 100; // TODO
  91. };
  92. function get_counter()
  93. {
  94. if(hpet_enabled)
  95. {
  96. return (Date.now() - hpet_start) * HPET_FREQ_MS + hpet_offset_low | 0;
  97. }
  98. else
  99. {
  100. return hpet_offset_low;
  101. }
  102. }
  103. function get_counter_high()
  104. {
  105. if(HPET_SUPPORT_64)
  106. {
  107. if(hpet_enabled)
  108. {
  109. return (Date.now() - hpet_start) * (HPET_FREQ_MS / 0x100000000) + hpet_offset_high | 0;
  110. }
  111. else
  112. {
  113. return hpet_offset_high;
  114. }
  115. }
  116. else
  117. {
  118. return 0;
  119. }
  120. }
  121. cpu.io.mmap_register(HPET_ADDR, 0x4000, mmio_read, mmio_write);
  122. function mmio_read(addr)
  123. {
  124. dbg_log("Read " + h(addr, 4) + " (ctr=" + h(get_counter() >>> 0) + ")", LOG_HPET);
  125. switch(addr)
  126. {
  127. case 0:
  128. return 1 << 16 | HPET_NUM_COUNTERS - 1 << 8 | 0x8000 | 0x01 | HPET_SUPPORT_64 << 13;
  129. case 4:
  130. return HPET_PERIOD;
  131. case 0x10:
  132. return me.legacy_mode << 1 | hpet_enabled;
  133. case 0xF0:
  134. return get_counter();
  135. case 0xF4:
  136. return get_counter_high();
  137. }
  138. // read from counter register
  139. var register = addr >> 2 & 7,
  140. counter = addr - 0x100 >> 5;
  141. if(addr < 0x100 || counter >= HPET_NUM_COUNTERS || register > 5)
  142. {
  143. dbg_log("Read reserved address: " + h(addr), LOG_HPET);
  144. return 0;
  145. }
  146. dbg_log("Read counter: addr=" + h(addr) + " counter=" + h(counter, 2) +
  147. " reg=" + h(register), LOG_HPET);
  148. switch(register)
  149. {
  150. case 0:
  151. return counter_config[counter << 1] & ~HPET_COUNTER_CONFIG_MASK | HPET_COUNTER_CONFIG;
  152. case 1:
  153. return counter_config[counter << 1 | 1];
  154. case 2:
  155. return counter_comparator[counter << 1];
  156. case 3:
  157. return counter_comparator[counter << 1 | 1];
  158. case 4:
  159. case 5:
  160. // TODO interrupt route register
  161. return 0;
  162. }
  163. }
  164. function mmio_write(addr, data)
  165. {
  166. dbg_log("Write " + h(addr, 4) + ": " + h(data, 2), LOG_HPET);
  167. switch(addr)
  168. {
  169. case 0x10:
  170. dbg_log("conf: enabled=" + (data & 1) + " legacy=" + (data >> 1 & 1), LOG_HPET);
  171. if((hpet_enabled ^ data) & 1)
  172. {
  173. if(data & 1)
  174. {
  175. // counter is enabled now, start counting now
  176. hpet_start = Date.now();
  177. }
  178. else
  179. {
  180. // counter is disabled now, save current count
  181. hpet_offset_low = get_counter();
  182. hpet_offset_high = get_counter_high();
  183. }
  184. }
  185. hpet_enabled = (data & 1) === 1;
  186. me.legacy_mode = (data & 2) === 2;
  187. return;
  188. case 0x20:
  189. // writing a 1 clears bits
  190. interrupt_status &= ~data;
  191. return;
  192. case 0xF0:
  193. hpet_offset_low = data;
  194. return;
  195. case 0xF4:
  196. hpet_offset_high = data;
  197. return;
  198. }
  199. // read from counter register
  200. var register = addr >> 2 & 7,
  201. counter = addr - 0x100 >> 5;
  202. if(addr < 0x100 || counter >= HPET_NUM_COUNTERS || register > 2)
  203. {
  204. dbg_log("Write reserved address: " + h(addr) + " data=" + h(data), LOG_HPET);
  205. return;
  206. }
  207. dbg_log("Write counter: addr=" + h(addr) + " counter=" + h(counter, 2) +
  208. " reg=" + h(register) + " data=" + h(data, 2), LOG_HPET);
  209. switch(register)
  210. {
  211. case 0:
  212. counter_config[counter << 1] = data;
  213. break;
  214. case 1:
  215. //counter_config[counter << 1 | 1] = data;
  216. break;
  217. case 2:
  218. if(counter_read_acc_next)
  219. {
  220. counter_accumulator[counter << 1] = data;
  221. counter_read_acc_next = false;
  222. dbg_log("Accumulator acc=" + h(data >>> 0, 8) + " ctr=" + h(counter, 2), LOG_HPET);
  223. }
  224. else
  225. {
  226. counter_comparator[counter << 1] = data;
  227. if(counter_config[counter << 1] & 1 << 6)
  228. {
  229. counter_read_acc_next = true;
  230. counter_config[counter << 1] &= ~(1 << 6);
  231. }
  232. }
  233. break;
  234. case 3:
  235. counter_comparator[counter << 1 | 1] = data;
  236. break;
  237. case 4:
  238. case 5:
  239. // TODO interrupt route register
  240. }
  241. }
  242. }