srtc.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #include <../base.hpp>
  2. #include <../cart/cart.hpp>
  3. #include "srtc.hpp"
  4. const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  5. void SRTC::init() {
  6. }
  7. void SRTC::enable() {
  8. memory::mmio.map(0x2800, *this);
  9. memory::mmio.map(0x2801, *this);
  10. }
  11. void SRTC::power() {
  12. reset();
  13. }
  14. void SRTC::reset() {
  15. rtc_mode = RTCM_Read;
  16. rtc_index = -1;
  17. update_time();
  18. }
  19. void SRTC::update_time() {
  20. time_t rtc_time
  21. = (memory::cartrtc.read(16) << 0)
  22. | (memory::cartrtc.read(17) << 8)
  23. | (memory::cartrtc.read(18) << 16)
  24. | (memory::cartrtc.read(19) << 24);
  25. time_t current_time = time(0);
  26. //sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic.
  27. //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
  28. //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow
  29. //memory::cartrtc timestamp to remain valid for up to ~34 years from the last update, even if
  30. //time_t overflows. calculation should be valid regardless of number representation, time_t size,
  31. //or whether time_t is signed or unsigned.
  32. time_t diff
  33. = (current_time >= rtc_time)
  34. ? (current_time - rtc_time)
  35. : (std::numeric_limits<time_t>::max() - rtc_time + current_time + 1); //compensate for overflow
  36. if(diff > std::numeric_limits<time_t>::max() / 2) diff = 0; //compensate for underflow
  37. if(diff > 0) {
  38. unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
  39. unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10;
  40. unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10;
  41. unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
  42. unsigned month = memory::cartrtc.read( 8);
  43. unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
  44. unsigned weekday = memory::cartrtc.read(12);
  45. day--;
  46. month--;
  47. year += 1000;
  48. second += diff;
  49. while(second >= 60) {
  50. second -= 60;
  51. minute++;
  52. if(minute < 60) continue;
  53. minute = 0;
  54. hour++;
  55. if(hour < 24) continue;
  56. hour = 0;
  57. day++;
  58. weekday = (weekday + 1) % 7;
  59. unsigned days = months[month % 12];
  60. if(days == 28) {
  61. bool leapyear = false;
  62. if((year % 4) == 0) {
  63. leapyear = true;
  64. if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
  65. }
  66. if(leapyear) days++;
  67. }
  68. if(day < days) continue;
  69. day = 0;
  70. month++;
  71. if(month < 12) continue;
  72. month = 0;
  73. year++;
  74. }
  75. day++;
  76. month++;
  77. year -= 1000;
  78. memory::cartrtc.write( 0, second % 10);
  79. memory::cartrtc.write( 1, second / 10);
  80. memory::cartrtc.write( 2, minute % 10);
  81. memory::cartrtc.write( 3, minute / 10);
  82. memory::cartrtc.write( 4, hour % 10);
  83. memory::cartrtc.write( 5, hour / 10);
  84. memory::cartrtc.write( 6, day % 10);
  85. memory::cartrtc.write( 7, day / 10);
  86. memory::cartrtc.write( 8, month);
  87. memory::cartrtc.write( 9, year % 10);
  88. memory::cartrtc.write(10, (year / 10) % 10);
  89. memory::cartrtc.write(11, year / 100);
  90. memory::cartrtc.write(12, weekday % 7);
  91. }
  92. memory::cartrtc.write(16, current_time >> 0);
  93. memory::cartrtc.write(17, current_time >> 8);
  94. memory::cartrtc.write(18, current_time >> 16);
  95. memory::cartrtc.write(19, current_time >> 24);
  96. }
  97. //returns day of week for specified date
  98. //eg 0 = Sunday, 1 = Monday, ... 6 = Saturday
  99. //usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008
  100. unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) {
  101. unsigned y = 1900, m = 1; //epoch is 1900-01-01
  102. unsigned sum = 0; //number of days passed since epoch
  103. year = max(1900, year);
  104. month = max(1, min(12, month));
  105. day = max(1, min(31, day));
  106. while(y < year) {
  107. bool leapyear = false;
  108. if((y % 4) == 0) {
  109. leapyear = true;
  110. if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
  111. }
  112. sum += leapyear ? 366 : 365;
  113. y++;
  114. }
  115. while(m < month) {
  116. unsigned days = months[m - 1];
  117. if(days == 28) {
  118. bool leapyear = false;
  119. if((y % 4) == 0) {
  120. leapyear = true;
  121. if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
  122. }
  123. if(leapyear) days++;
  124. }
  125. sum += days;
  126. m++;
  127. }
  128. sum += day - 1;
  129. return (sum + 1) % 7; //1900-01-01 was a Monday
  130. }
  131. uint8 SRTC::mmio_read(unsigned addr) {
  132. addr &= 0xffff;
  133. if(addr == 0x2800) {
  134. if(rtc_mode != RTCM_Read) return 0x00;
  135. if(rtc_index < 0) {
  136. update_time();
  137. rtc_index++;
  138. return 0x0f;
  139. } else if(rtc_index > 12) {
  140. rtc_index = -1;
  141. return 0x0f;
  142. } else {
  143. return memory::cartrtc.read(rtc_index++);
  144. }
  145. }
  146. return cpu.regs.mdr;
  147. }
  148. void SRTC::mmio_write(unsigned addr, uint8 data) {
  149. addr &= 0xffff;
  150. if(addr == 0x2801) {
  151. data &= 0x0f; //only the low four bits are used
  152. if(data == 0x0d) {
  153. rtc_mode = RTCM_Read;
  154. rtc_index = -1;
  155. return;
  156. }
  157. if(data == 0x0e) {
  158. rtc_mode = RTCM_Command;
  159. return;
  160. }
  161. if(data == 0x0f) return; //unknown behavior
  162. if(rtc_mode == RTCM_Write) {
  163. if(rtc_index >= 0 && rtc_index < 12) {
  164. memory::cartrtc.write(rtc_index++, data);
  165. if(rtc_index == 12) {
  166. //day of week is automatically calculated and written
  167. unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
  168. unsigned month = memory::cartrtc.read( 8);
  169. unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
  170. year += 1000;
  171. memory::cartrtc.write(rtc_index++, weekday(year, month, day));
  172. }
  173. }
  174. } else if(rtc_mode == RTCM_Command) {
  175. if(data == 0) {
  176. rtc_mode = RTCM_Write;
  177. rtc_index = 0;
  178. } else if(data == 4) {
  179. rtc_mode = RTCM_Ready;
  180. rtc_index = -1;
  181. for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0);
  182. } else {
  183. //unknown behavior
  184. rtc_mode = RTCM_Ready;
  185. }
  186. }
  187. }
  188. }
  189. SRTC::SRTC() {
  190. }