rtc.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. "use strict";
  2. /** @const */ var CMOS_RTC_SECONDS = 0x00;
  3. /** @const */ var CMOS_RTC_SECONDS_ALARM = 0x01;
  4. /** @const */ var CMOS_RTC_MINUTES = 0x02;
  5. /** @const */ var CMOS_RTC_MINUTES_ALARM = 0x03;
  6. /** @const */ var CMOS_RTC_HOURS = 0x04;
  7. /** @const */ var CMOS_RTC_HOURS_ALARM = 0x05;
  8. /** @const */ var CMOS_RTC_DAY_WEEK = 0x06;
  9. /** @const */ var CMOS_RTC_DAY_MONTH = 0x07;
  10. /** @const */ var CMOS_RTC_MONTH = 0x08;
  11. /** @const */ var CMOS_RTC_YEAR = 0x09;
  12. /** @const */ var CMOS_STATUS_A = 0x0a;
  13. /** @const */ var CMOS_STATUS_B = 0x0b;
  14. /** @const */ var CMOS_STATUS_C = 0x0c;
  15. /** @const */ var CMOS_STATUS_D = 0x0d;
  16. /** @const */ var CMOS_RESET_CODE = 0x0f;
  17. /** @const */ var CMOS_FLOPPY_DRIVE_TYPE = 0x10;
  18. /** @const */ var CMOS_DISK_DATA = 0x12;
  19. /** @const */ var CMOS_EQUIPMENT_INFO = 0x14;
  20. /** @const */ var CMOS_MEM_BASE_LOW = 0x15;
  21. /** @const */ var CMOS_MEM_BASE_HIGH = 0x16;
  22. /** @const */ var CMOS_MEM_OLD_EXT_LOW = 0x17;
  23. /** @const */ var CMOS_MEM_OLD_EXT_HIGH = 0x18;
  24. /** @const */ var CMOS_DISK_DRIVE1_TYPE = 0x19;
  25. /** @const */ var CMOS_DISK_DRIVE2_TYPE = 0x1a;
  26. /** @const */ var CMOS_DISK_DRIVE1_CYL = 0x1b;
  27. /** @const */ var CMOS_DISK_DRIVE2_CYL = 0x24;
  28. /** @const */ var CMOS_MEM_EXTMEM_LOW = 0x30;
  29. /** @const */ var CMOS_MEM_EXTMEM_HIGH = 0x31;
  30. /** @const */ var CMOS_CENTURY = 0x32;
  31. /** @const */ var CMOS_MEM_EXTMEM2_LOW = 0x34;
  32. /** @const */ var CMOS_MEM_EXTMEM2_HIGH = 0x35;
  33. /** @const */ var CMOS_BIOS_BOOTFLAG1 = 0x38;
  34. /** @const */ var CMOS_BIOS_DISKTRANSFLAG = 0x39;
  35. /** @const */ var CMOS_BIOS_BOOTFLAG2 = 0x3d;
  36. /** @const */ var CMOS_MEM_HIGHMEM_LOW = 0x5b;
  37. /** @const */ var CMOS_MEM_HIGHMEM_MID = 0x5c;
  38. /** @const */ var CMOS_MEM_HIGHMEM_HIGH = 0x5d;
  39. /** @const */ var CMOS_BIOS_SMP_COUNT = 0x5f;
  40. /**
  41. * RTC (real time clock) and CMOS
  42. * @constructor
  43. * @param {CPU} cpu
  44. */
  45. function RTC(cpu)
  46. {
  47. /** @const @type {CPU} */
  48. this.cpu = cpu;
  49. this.cmos_index = 0;
  50. this.cmos_data = new Uint8Array(128);
  51. // used for cmos entries
  52. this.rtc_time = Date.now();
  53. this.last_update = this.rtc_time;
  54. // used for periodic interrupt
  55. this.next_interrupt = 0;
  56. // next alarm interrupt
  57. this.next_interrupt_alarm = 0;
  58. this.periodic_interrupt = false;
  59. // corresponds to default value for cmos_a
  60. this.periodic_interrupt_time = 1000 / 1024;
  61. this.cmos_a = 0x26;
  62. this.cmos_b = 2;
  63. this.cmos_c = 0;
  64. this.nmi_disabled = 0;
  65. cpu.io.register_write(0x70, this, function(out_byte)
  66. {
  67. this.cmos_index = out_byte & 0x7F;
  68. this.nmi_disabled = out_byte >> 7;
  69. });
  70. cpu.io.register_write(0x71, this, this.cmos_port_write);
  71. cpu.io.register_read(0x71, this, this.cmos_port_read);
  72. }
  73. RTC.prototype.get_state = function()
  74. {
  75. var state = [];
  76. state[0] = this.cmos_index;
  77. state[1] = this.cmos_data;
  78. state[2] = this.rtc_time;
  79. state[3] = this.last_update;
  80. state[4] = this.next_interrupt;
  81. state[5] = this.next_interrupt_alarm;
  82. state[6] = this.periodic_interrupt;
  83. state[7] = this.periodic_interrupt_time;
  84. state[8] = this.cmos_a;
  85. state[9] = this.cmos_b;
  86. state[10] = this.cmos_c;
  87. state[11] = this.nmi_disabled;
  88. return state;
  89. };
  90. RTC.prototype.set_state = function(state)
  91. {
  92. this.cmos_index = state[0];
  93. this.cmos_data = state[1];
  94. this.rtc_time = state[2];
  95. this.last_update = state[3];
  96. this.next_interrupt = state[4];
  97. this.next_interrupt_alarm = state[5];
  98. this.periodic_interrupt = state[6];
  99. this.periodic_interrupt_time = state[7];
  100. this.cmos_a = state[8];
  101. this.cmos_b = state[9];
  102. this.cmos_c = state[10];
  103. this.nmi_disabled = state[11];
  104. };
  105. RTC.prototype.timer = function(time, legacy_mode)
  106. {
  107. time = Date.now(); // XXX
  108. this.rtc_time += time - this.last_update;
  109. this.last_update = time;
  110. if(this.periodic_interrupt && this.next_interrupt < time)
  111. {
  112. this.cpu.device_raise_irq(8);
  113. this.cmos_c |= 1 << 6 | 1 << 7;
  114. this.next_interrupt += this.periodic_interrupt_time *
  115. Math.ceil((time - this.next_interrupt) / this.periodic_interrupt_time);
  116. }
  117. else if(this.next_interrupt_alarm && this.next_interrupt_alarm < time)
  118. {
  119. this.cpu.device_raise_irq(8);
  120. this.cmos_c |= 1 << 5 | 1 << 7;
  121. this.next_interrupt_alarm = 0;
  122. }
  123. let t = 100;
  124. if(this.periodic_interrupt && this.next_interrupt)
  125. {
  126. t = Math.min(t, Math.max(0, this.next_interrupt - time));
  127. }
  128. if(this.next_interrupt_alarm)
  129. {
  130. t = Math.min(t, Math.max(0, this.next_interrupt_alarm - time));
  131. }
  132. return t;
  133. };
  134. RTC.prototype.bcd_pack = function(n)
  135. {
  136. var i = 0,
  137. result = 0,
  138. digit;
  139. while(n)
  140. {
  141. digit = n % 10;
  142. result |= digit << (4 * i);
  143. i++;
  144. n = (n - digit) / 10;
  145. }
  146. return result;
  147. };
  148. RTC.prototype.bcd_unpack = function(n)
  149. {
  150. const low = n & 0xF;
  151. const high = n >> 4 & 0xF;
  152. dbg_assert(n < 0x100);
  153. dbg_assert(low < 10);
  154. dbg_assert(high < 10);
  155. return low + 10 * high;
  156. };
  157. RTC.prototype.encode_time = function(t)
  158. {
  159. if(this.cmos_b & 4)
  160. {
  161. // binary mode
  162. return t;
  163. }
  164. else
  165. {
  166. return this.bcd_pack(t);
  167. }
  168. };
  169. RTC.prototype.decode_time = function(t)
  170. {
  171. if(this.cmos_b & 4)
  172. {
  173. // binary mode
  174. return t;
  175. }
  176. else
  177. {
  178. return this.bcd_unpack(t);
  179. }
  180. };
  181. // TODO
  182. // - interrupt on update
  183. // - countdown
  184. // - letting bios/os set values
  185. // (none of these are used by seabios or the OSes we're
  186. // currently testing)
  187. RTC.prototype.cmos_port_read = function()
  188. {
  189. var index = this.cmos_index;
  190. //this.cmos_index = 0xD;
  191. switch(index)
  192. {
  193. case CMOS_RTC_SECONDS:
  194. return this.encode_time(new Date(this.rtc_time).getUTCSeconds());
  195. case CMOS_RTC_MINUTES:
  196. return this.encode_time(new Date(this.rtc_time).getUTCMinutes());
  197. case CMOS_RTC_HOURS:
  198. // TODO: 12 hour mode
  199. return this.encode_time(new Date(this.rtc_time).getUTCHours());
  200. case CMOS_RTC_DAY_MONTH:
  201. return this.encode_time(new Date(this.rtc_time).getUTCDate());
  202. case CMOS_RTC_MONTH:
  203. return this.encode_time(new Date(this.rtc_time).getUTCMonth() + 1);
  204. case CMOS_RTC_YEAR:
  205. return this.encode_time(new Date(this.rtc_time).getUTCFullYear() % 100);
  206. case CMOS_STATUS_A:
  207. if(v86.microtick() % 1000 >= 999)
  208. {
  209. // Set update-in-progress for one millisecond every second (we
  210. // may not have precision higher than that in browser
  211. // environments)
  212. return this.cmos_a | 0x80;
  213. }
  214. return this.cmos_a;
  215. case CMOS_STATUS_B:
  216. //dbg_log("cmos read from index " + h(index));
  217. return this.cmos_b;
  218. case CMOS_STATUS_C:
  219. // It is important to know that upon a IRQ 8, Status Register C
  220. // will contain a bitmask telling which interrupt happened.
  221. // What is important is that if register C is not read after an
  222. // IRQ 8, then the interrupt will not happen again.
  223. this.cpu.device_lower_irq(8);
  224. dbg_log("cmos reg C read", LOG_RTC);
  225. // Missing IRQF flag
  226. //return cmos_b & 0x70;
  227. var c = this.cmos_c;
  228. this.cmos_c &= ~0xF0;
  229. return c;
  230. case CMOS_STATUS_D:
  231. return 0;
  232. case CMOS_CENTURY:
  233. return this.encode_time(new Date(this.rtc_time).getUTCFullYear() / 100 | 0);
  234. default:
  235. dbg_log("cmos read from index " + h(index), LOG_RTC);
  236. return this.cmos_data[this.cmos_index];
  237. }
  238. };
  239. RTC.prototype.cmos_port_write = function(data_byte)
  240. {
  241. switch(this.cmos_index)
  242. {
  243. case 0xA:
  244. this.cmos_a = data_byte & 0x7F;
  245. this.periodic_interrupt_time = 1000 / (32768 >> (this.cmos_a & 0xF) - 1);
  246. dbg_log("Periodic interrupt, a=" + h(this.cmos_a, 2) + " t=" + this.periodic_interrupt_time , LOG_RTC);
  247. break;
  248. case 0xB:
  249. this.cmos_b = data_byte;
  250. if(this.cmos_b & 0x40)
  251. {
  252. this.next_interrupt = Date.now();
  253. }
  254. if(this.cmos_b & 0x20)
  255. {
  256. const now = new Date();
  257. const seconds = this.decode_time(this.cmos_data[CMOS_RTC_SECONDS_ALARM]);
  258. const minutes = this.decode_time(this.cmos_data[CMOS_RTC_MINUTES_ALARM]);
  259. const hours = this.decode_time(this.cmos_data[CMOS_RTC_HOURS_ALARM]);
  260. const alarm_date = new Date(Date.UTC(
  261. now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
  262. hours, minutes, seconds
  263. ));
  264. const ms_from_now = alarm_date - now;
  265. dbg_log("RTC alarm scheduled for " + alarm_date +
  266. " hh:mm:ss=" + hours + ":" + minutes + ":" + seconds +
  267. " ms_from_now=" + ms_from_now, LOG_RTC);
  268. this.next_interrupt_alarm = +alarm_date;
  269. }
  270. if(this.cmos_b & 0x10) dbg_log("Unimplemented: updated interrupt", LOG_RTC);
  271. dbg_log("cmos b=" + h(this.cmos_b, 2), LOG_RTC);
  272. break;
  273. case CMOS_RTC_SECONDS_ALARM:
  274. case CMOS_RTC_MINUTES_ALARM:
  275. case CMOS_RTC_HOURS_ALARM:
  276. this.cmos_write(this.cmos_index, data_byte);
  277. break;
  278. default:
  279. dbg_log("cmos write index " + h(this.cmos_index) + ": " + h(data_byte), LOG_RTC);
  280. }
  281. this.periodic_interrupt = (this.cmos_b & 0x40) === 0x40 && (this.cmos_a & 0xF) > 0;
  282. };
  283. /**
  284. * @param {number} index
  285. */
  286. RTC.prototype.cmos_read = function(index)
  287. {
  288. dbg_assert(index < 128);
  289. return this.cmos_data[index];
  290. };
  291. /**
  292. * @param {number} index
  293. * @param {number} value
  294. */
  295. RTC.prototype.cmos_write = function(index, value)
  296. {
  297. dbg_log("cmos " + h(index) + " <- " + h(value), LOG_RTC);
  298. dbg_assert(index < 128);
  299. this.cmos_data[index] = value;
  300. };