rtc.js 10 KB

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