hpet.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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;
  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. };
  91. function get_counter()
  92. {
  93. if(hpet_enabled)
  94. {
  95. return (Date.now() - hpet_start) * HPET_FREQ_MS + hpet_offset_low | 0;
  96. }
  97. else
  98. {
  99. return hpet_offset_low;
  100. }
  101. }
  102. function get_counter_high()
  103. {
  104. if(HPET_SUPPORT_64)
  105. {
  106. if(hpet_enabled)
  107. {
  108. return (Date.now() - hpet_start) * (HPET_FREQ_MS / 0x100000000) + hpet_offset_high | 0;
  109. }
  110. else
  111. {
  112. return hpet_offset_high;
  113. }
  114. }
  115. else
  116. {
  117. return 0;
  118. }
  119. }
  120. cpu.io.mmap_register(HPET_ADDR, 0x4000, mmio_read, mmio_write);
  121. function mmio_read(addr)
  122. {
  123. dbg_log("Read " + h(addr, 4) + " (ctr=" + h(get_counter() >>> 0) + ")", LOG_HPET);
  124. switch(addr)
  125. {
  126. case 0:
  127. return 1 << 16 | HPET_NUM_COUNTERS - 1 << 8 | 0x8000 | 0x01 | HPET_SUPPORT_64 << 13;
  128. case 4:
  129. return HPET_PERIOD;
  130. case 0x10:
  131. return me.legacy_mode << 1 | hpet_enabled;
  132. case 0xF0:
  133. return get_counter();
  134. case 0xF4:
  135. return get_counter_high();
  136. }
  137. // read from counter register
  138. var register = addr >> 2 & 7,
  139. counter = addr - 0x100 >> 5;
  140. if(addr < 0x100 || counter >= HPET_NUM_COUNTERS || register > 5)
  141. {
  142. dbg_log("Read reserved address: " + h(addr), LOG_HPET);
  143. return 0;
  144. }
  145. dbg_log("Read counter: addr=" + h(addr) + " counter=" + h(counter, 2) +
  146. " reg=" + h(register), LOG_HPET);
  147. switch(register)
  148. {
  149. case 0:
  150. return counter_config[counter << 1] & ~HPET_COUNTER_CONFIG_MASK | HPET_COUNTER_CONFIG;
  151. case 1:
  152. return counter_config[counter << 1 | 1];
  153. case 2:
  154. return counter_comparator[counter << 1];
  155. case 3:
  156. return counter_comparator[counter << 1 | 1];
  157. case 4:
  158. case 5:
  159. // TODO interrupt route register
  160. return 0;
  161. }
  162. }
  163. function mmio_write(addr, data)
  164. {
  165. dbg_log("Write " + h(addr, 4) + ": " + h(data, 2), LOG_HPET);
  166. switch(addr)
  167. {
  168. case 0x10:
  169. dbg_log("conf: enabled=" + (data & 1) + " legacy=" + (data >> 1 & 1), LOG_HPET);
  170. if((hpet_enabled ^ data) & 1)
  171. {
  172. if(data & 1)
  173. {
  174. // counter is enabled now, start counting now
  175. hpet_start = Date.now();
  176. }
  177. else
  178. {
  179. // counter is disabled now, save current count
  180. hpet_offset_low = get_counter();
  181. hpet_offset_high = get_counter_high();
  182. }
  183. }
  184. hpet_enabled = (data & 1) === 1;
  185. me.legacy_mode = (data & 2) === 2;
  186. return;
  187. case 0x20:
  188. // writing a 1 clears bits
  189. interrupt_status &= ~data;
  190. return;
  191. case 0xF0:
  192. hpet_offset_low = data;
  193. return;
  194. case 0xF4:
  195. hpet_offset_high = data;
  196. return;
  197. }
  198. // read from counter register
  199. var register = addr >> 2 & 7,
  200. counter = addr - 0x100 >> 5;
  201. if(addr < 0x100 || counter >= HPET_NUM_COUNTERS || register > 2)
  202. {
  203. dbg_log("Write reserved address: " + h(addr) + " data=" + h(data), LOG_HPET);
  204. return;
  205. }
  206. dbg_log("Write counter: addr=" + h(addr) + " counter=" + h(counter, 2) +
  207. " reg=" + h(register) + " data=" + h(data, 2), LOG_HPET);
  208. switch(register)
  209. {
  210. case 0:
  211. counter_config[counter << 1] = data;
  212. break;
  213. case 1:
  214. //counter_config[counter << 1 | 1] = data;
  215. break;
  216. case 2:
  217. if(counter_read_acc_next)
  218. {
  219. counter_accumulator[counter << 1] = data;
  220. counter_read_acc_next = false;
  221. dbg_log("Accumulator acc=" + h(data >>> 0, 8) + " ctr=" + h(counter, 2), LOG_HPET);
  222. }
  223. else
  224. {
  225. counter_comparator[counter << 1] = data;
  226. if(counter_config[counter << 1] & 1 << 6)
  227. {
  228. counter_read_acc_next = true;
  229. counter_config[counter << 1] &= ~(1 << 6);
  230. }
  231. }
  232. break;
  233. case 3:
  234. counter_comparator[counter << 1 | 1] = data;
  235. break;
  236. case 4:
  237. case 5:
  238. // TODO interrupt route register
  239. }
  240. }
  241. }