annotate src/gba/RTC.cpp @ 92:1ff2c546f5ad

added tick(), which allows one to step through each opcode of gameboy
author Robert McIntyre <rlm@mit.edu>
date Sun, 11 Mar 2012 19:07:31 -0500
parents f9f4f1b99eed
children
rev   line source
rlm@1 1 #include <cstring>
rlm@1 2
rlm@1 3 #include "../Port.h"
rlm@1 4 #include "../NLS.h"
rlm@1 5 #include "../common/System.h" // systemMessage
rlm@1 6 #include "../common/Util.h"
rlm@1 7 #include "../common/movie.h"
rlm@1 8 #include "GBAGlobals.h"
rlm@1 9
rlm@1 10 enum RTCSTATE { IDLE, COMMAND, DATA, READDATA };
rlm@1 11
rlm@1 12 typedef struct
rlm@1 13 {
rlm@1 14 u8 byte0;
rlm@1 15 u8 byte1;
rlm@1 16 u8 byte2;
rlm@1 17 u8 command;
rlm@1 18 int dataLen;
rlm@1 19 int bits;
rlm@1 20 RTCSTATE state;
rlm@1 21 u8 data[12];
rlm@1 22 // reserved variables for future
rlm@1 23 u8 reserved[12];
rlm@1 24 bool reserved2;
rlm@1 25 u32 reserved3;
rlm@1 26 } RTCCLOCKDATA;
rlm@1 27
rlm@1 28 static RTCCLOCKDATA rtcClockData;
rlm@1 29 static bool rtcEnabled = false;
rlm@1 30
rlm@1 31 void rtcEnable(bool enable)
rlm@1 32 {
rlm@1 33 rtcEnabled = enable;
rlm@1 34 }
rlm@1 35
rlm@1 36 bool rtcIsEnabled()
rlm@1 37 {
rlm@1 38 return rtcEnabled;
rlm@1 39 }
rlm@1 40
rlm@1 41 u16 rtcRead(u32 address)
rlm@1 42 {
rlm@1 43 if (rtcEnabled)
rlm@1 44 {
rlm@1 45 if (address == 0x80000c8)
rlm@1 46 return rtcClockData.byte2;
rlm@1 47 else if (address == 0x80000c6)
rlm@1 48 return rtcClockData.byte1;
rlm@1 49 else if (address == 0x80000c4)
rlm@1 50 {
rlm@1 51 return rtcClockData.byte0;
rlm@1 52 }
rlm@1 53 }
rlm@1 54
rlm@1 55 return READ16LE((&rom[address & 0x1FFFFFE]));
rlm@1 56 }
rlm@1 57
rlm@1 58 static u8 toBCD(u8 value)
rlm@1 59 {
rlm@1 60 value = value % 100;
rlm@1 61 int l = value % 10;
rlm@1 62 int h = value / 10;
rlm@1 63 return h * 16 + l;
rlm@1 64 }
rlm@1 65
rlm@1 66 bool rtcWrite(u32 address, u16 value)
rlm@1 67 {
rlm@1 68 if (!rtcEnabled)
rlm@1 69 return false;
rlm@1 70
rlm@1 71 if (address == 0x80000c8)
rlm@1 72 {
rlm@1 73 rtcClockData.byte2 = (u8)value; // enable ?
rlm@1 74 }
rlm@1 75 else if (address == 0x80000c6)
rlm@1 76 {
rlm@1 77 rtcClockData.byte1 = (u8)value; // read/write
rlm@1 78 }
rlm@1 79 else if (address == 0x80000c4)
rlm@1 80 {
rlm@1 81 if (rtcClockData.byte2 & 1)
rlm@1 82 {
rlm@1 83 if (rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5)
rlm@1 84 {
rlm@1 85 rtcClockData.state = COMMAND;
rlm@1 86 rtcClockData.bits = 0;
rlm@1 87 rtcClockData.command = 0;
rlm@1 88 }
rlm@1 89 else if (!(rtcClockData.byte0 & 1) && (value & 1)) // bit transfer
rlm@1 90 {
rlm@1 91 rtcClockData.byte0 = (u8)value;
rlm@1 92 switch (rtcClockData.state)
rlm@1 93 {
rlm@1 94 case COMMAND:
rlm@1 95 rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits);
rlm@1 96 rtcClockData.bits++;
rlm@1 97 if (rtcClockData.bits == 8)
rlm@1 98 {
rlm@1 99 rtcClockData.bits = 0;
rlm@1 100 switch (rtcClockData.command)
rlm@1 101 {
rlm@1 102 case 0x60:
rlm@1 103 // not sure what this command does but it doesn't take parameters
rlm@1 104 // maybe it is a reset or stop
rlm@1 105 rtcClockData.state = IDLE;
rlm@1 106 rtcClockData.bits = 0;
rlm@1 107 break;
rlm@1 108 case 0x62:
rlm@1 109 // this sets the control state but not sure what those values are
rlm@1 110 rtcClockData.state = READDATA;
rlm@1 111 rtcClockData.dataLen = 1;
rlm@1 112 break;
rlm@1 113 case 0x63:
rlm@1 114 rtcClockData.dataLen = 1;
rlm@1 115 rtcClockData.data[0] = 0x40;
rlm@1 116 rtcClockData.state = DATA;
rlm@1 117 break;
rlm@1 118 case 0x65:
rlm@1 119 {
rlm@1 120 struct tm *newtime;
rlm@1 121 time_t long_time;
rlm@1 122
rlm@1 123 if (VBAMovieActive() || VBAMovieLoading())
rlm@1 124 {
rlm@1 125 long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60;
rlm@1 126 newtime = gmtime(&long_time);
rlm@1 127 }
rlm@1 128 else
rlm@1 129 {
rlm@1 130 time(&long_time); /* Get time as long integer. */
rlm@1 131 newtime = localtime(&long_time); /* Convert to local time. */
rlm@1 132 }
rlm@1 133
rlm@1 134 rtcClockData.dataLen = 7;
rlm@1 135 rtcClockData.data[0] = toBCD(newtime->tm_year);
rlm@1 136 rtcClockData.data[1] = toBCD(newtime->tm_mon+1);
rlm@1 137 rtcClockData.data[2] = toBCD(newtime->tm_mday);
rlm@1 138 rtcClockData.data[3] = 0;
rlm@1 139 rtcClockData.data[4] = toBCD(newtime->tm_hour);
rlm@1 140 rtcClockData.data[5] = toBCD(newtime->tm_min);
rlm@1 141 rtcClockData.data[6] = toBCD(newtime->tm_sec);
rlm@1 142 rtcClockData.state = DATA;
rlm@1 143 break;
rlm@1 144 }
rlm@1 145 case 0x67:
rlm@1 146 {
rlm@1 147 struct tm *newtime;
rlm@1 148 time_t long_time;
rlm@1 149
rlm@1 150 if (VBAMovieActive() || VBAMovieLoading())
rlm@1 151 {
rlm@1 152 long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60;
rlm@1 153 newtime = gmtime(&long_time);
rlm@1 154 }
rlm@1 155 else
rlm@1 156 {
rlm@1 157 time(&long_time); /* Get time as long integer. */
rlm@1 158 newtime = localtime(&long_time); /* Convert to local time. */
rlm@1 159 }
rlm@1 160
rlm@1 161 rtcClockData.dataLen = 3;
rlm@1 162 rtcClockData.data[0] = toBCD(newtime->tm_hour);
rlm@1 163 rtcClockData.data[1] = toBCD(newtime->tm_min);
rlm@1 164 rtcClockData.data[2] = toBCD(newtime->tm_sec);
rlm@1 165 rtcClockData.state = DATA;
rlm@1 166 break;
rlm@1 167 }
rlm@1 168 default:
rlm@1 169 systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command);
rlm@1 170 rtcClockData.state = IDLE;
rlm@1 171 break;
rlm@1 172 }
rlm@1 173 }
rlm@1 174 break;
rlm@1 175 case DATA:
rlm@1 176 if (rtcClockData.byte1 & 2)
rlm@1 177 {}
rlm@1 178 else
rlm@1 179 {
rlm@1 180 rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
rlm@1 181 ((rtcClockData.data[rtcClockData.bits >> 3] >>
rlm@1 182 (rtcClockData.bits & 7)) & 1)*2;
rlm@1 183 rtcClockData.bits++;
rlm@1 184 if (rtcClockData.bits == 8*rtcClockData.dataLen)
rlm@1 185 {
rlm@1 186 rtcClockData.bits = 0;
rlm@1 187 rtcClockData.state = IDLE;
rlm@1 188 }
rlm@1 189 }
rlm@1 190 break;
rlm@1 191 case READDATA:
rlm@1 192 if (!(rtcClockData.byte1 & 2))
rlm@1 193 {}
rlm@1 194 else
rlm@1 195 {
rlm@1 196 rtcClockData.data[rtcClockData.bits >> 3] =
rlm@1 197 (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
rlm@1 198 ((value << 6) & 128);
rlm@1 199 rtcClockData.bits++;
rlm@1 200 if (rtcClockData.bits == 8*rtcClockData.dataLen)
rlm@1 201 {
rlm@1 202 rtcClockData.bits = 0;
rlm@1 203 rtcClockData.state = IDLE;
rlm@1 204 }
rlm@1 205 }
rlm@1 206 break;
rlm@1 207 default:
rlm@1 208 break;
rlm@1 209 }
rlm@1 210 }
rlm@1 211 else
rlm@1 212 rtcClockData.byte0 = (u8)value;
rlm@1 213 }
rlm@1 214 }
rlm@1 215 return true;
rlm@1 216 }
rlm@1 217
rlm@1 218 void rtcReset()
rlm@1 219 {
rlm@1 220 memset(&rtcClockData, 0, sizeof(rtcClockData));
rlm@1 221
rlm@1 222 rtcClockData.byte0 = 0;
rlm@1 223 rtcClockData.byte1 = 0;
rlm@1 224 rtcClockData.byte2 = 0;
rlm@1 225 rtcClockData.command = 0;
rlm@1 226 rtcClockData.dataLen = 0;
rlm@1 227 rtcClockData.bits = 0;
rlm@1 228 rtcClockData.state = IDLE;
rlm@1 229 }
rlm@1 230
rlm@1 231 void rtcSaveGame(gzFile gzFile)
rlm@1 232 {
rlm@1 233 utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData));
rlm@1 234 }
rlm@1 235
rlm@1 236 void rtcReadGame(gzFile gzFile)
rlm@1 237 {
rlm@1 238 utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData));
rlm@1 239 }
rlm@1 240