workaround a problem with the harmattan gcc
[drnoksnes] / srtc.cpp
1 /*
2  * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3  *
4  * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5  *                           Jerremy Koot (jkoot@snes9x.com)
6  *
7  * Super FX C emulator code 
8  * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
9  *                           Gary Henderson.
10  * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
11  *
12  * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
13  * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
14  * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
15  *
16  * DOS port code contains the works of other authors. See headers in
17  * individual files.
18  *
19  * Snes9x homepage: http://www.snes9x.com
20  *
21  * Permission to use, copy, modify and distribute Snes9x in both binary and
22  * source form, for non-commercial purposes, is hereby granted without fee,
23  * providing that this license information and copyright notice appear with
24  * all copies and any derived work.
25  *
26  * This software is provided 'as-is', without any express or implied
27  * warranty. In no event shall the authors be held liable for any damages
28  * arising from the use of this software.
29  *
30  * Snes9x is freeware for PERSONAL USE only. Commercial users should
31  * seek permission of the copyright holders first. Commercial use includes
32  * charging money for Snes9x or software derived from Snes9x.
33  *
34  * The copyright holders request that bug fixes and improvements to the code
35  * should be forwarded to them so everyone can benefit from the modifications
36  * in future versions.
37  *
38  * Super NES and Super Nintendo Entertainment System are trademarks of
39  * Nintendo Co., Limited and its subsidiary companies.
40  */
41 #include <string.h>
42 #include <time.h>
43 #include "snes9x.h"
44 #include "srtc.h"
45 #include "memmap.h"
46
47 /***   The format of the rtc_data structure is:
48
49 Index Description     Range (nibble)
50 ----- --------------  ---------------------------------------
51
52   0   Seconds low     0-9
53   1   Seconds high    0-5
54
55   2   Minutes low     0-9
56   3   Minutes high    0-5
57
58   4   Hour low        0-9
59   5   Hour high       0-2
60
61   6   Day low         0-9
62   7   Day high        0-3
63
64   8   Month           1-C (0xC is December, 12th month)
65
66   9   Year ones       0-9
67   A   Year tens       0-9
68   B   Year High       9-B  (9=19xx, A=20xx, B=21xx)
69
70   C   Day of week     0-6  (0=Sunday, 1=Monday,...,6=Saturday)
71
72 ***/
73
74 SRTC_DATA           rtc;
75
76
77 static int month_keys[12] = { 1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6 };
78
79
80 /*********************************************************************************************
81  *
82  * Note, if you are doing a save state for this game:
83  *
84  * On save:
85  *
86  *      Call S9xUpdateSrtcTime and save the rtc data structure.
87  *
88  * On load:
89  *
90  *      restore the rtc data structure
91  *      rtc.system_timestamp = time (NULL);
92  *        
93  *
94  *********************************************************************************************/
95
96
97 void S9xResetSRTC ()
98 {
99     rtc.index = -1;
100     rtc.mode = MODE_READ;
101 }
102
103 void S9xHardResetSRTC ()
104 {
105     ZeroMemory (&rtc, sizeof (rtc));
106     rtc.index = -1;
107     rtc.mode = MODE_READ;
108     rtc.count_enable = FALSE;
109     rtc.needs_init = TRUE;
110
111     // Get system timestamp
112     rtc.system_timestamp = time (NULL);
113 }
114
115 /**********************************************************************************************/
116 /* S9xSRTCComputeDayOfWeek()                                                                  */
117 /* Return 0-6 for Sunday-Saturday                                                             */
118 /**********************************************************************************************/
119 unsigned int    S9xSRTCComputeDayOfWeek ()
120 {
121     unsigned    year = rtc.data[10]*10 + rtc.data[9];
122     unsigned    month = rtc.data[8];
123     unsigned    day = rtc.data[7]*10 + rtc.data[6];
124     unsigned    day_of_week;
125
126     year += (rtc.data[11] - 9) * 100;
127
128     // Range check the month for valid array indicies
129     if ( month > 12 )
130         month = 1;
131
132     day_of_week = year + (year / 4) + month_keys[month-1] + day - 1;
133
134     if(( year % 4 == 0 ) && ( month <= 2 ) )
135         day_of_week--;
136
137     day_of_week %= 7;
138
139     return day_of_week;
140 }
141
142
143 /**********************************************************************************************/
144 /* S9xSRTCDaysInMonth()                                                                       */
145 /* Return the number of days in a specific month for a certain year                           */
146 /**********************************************************************************************/
147 int     S9xSRTCDaysInMmonth( int month, int year )
148 {
149     int         mdays;
150
151     switch ( month )
152     {
153         case 2:
154                 if ( ( year % 4 == 0 ) )    // DKJM2 only uses 199x - 22xx
155                         mdays = 29;
156                 else
157                         mdays = 28;
158                 break;
159
160         case 4:
161         case 6:
162         case 9:
163         case 11:
164                 mdays = 30;
165                 break;
166
167         default:        // months 1,3,5,7,8,10,12
168                 mdays = 31;
169                 break;
170     }
171
172     return mdays;
173 }
174
175
176 #define DAYTICKS (60*60*24)
177 #define HOURTICKS (60*60)
178 #define MINUTETICKS 60
179
180
181 /**********************************************************************************************/
182 /* S9xUpdateSrtcTime()                                                                        */
183 /* Advance the  S-RTC time if counting is enabled                                             */
184 /**********************************************************************************************/
185 void    S9xUpdateSrtcTime ()
186 {
187         time_t  cur_systime;
188         long    time_diff;
189
190     // Keep track of game time by computing the number of seconds that pass on the system
191     // clock and adding the same number of seconds to the S-RTC clock structure.
192     // I originally tried using mktime and localtime library functions to keep track
193     // of time but some of the GNU time functions fail when the year goes to 2099
194     // (and maybe less) and this would have caused a bug with DKJM2 so I'm doing
195     // it this way to get around that problem.
196
197     // Note: Dai Kaijyu Monogatari II only allows dates in the range 1996-21xx.
198
199     if (rtc.count_enable && !rtc.needs_init)
200     {
201         cur_systime = time (NULL);
202
203         // This method assumes one time_t clock tick is one second
204         //        which should work on PCs and GNU systems.
205         //        If your tick interval is different adjust the
206         //        DAYTICK, HOURTICK, and MINUTETICK defines
207
208         time_diff = (long) (cur_systime - rtc.system_timestamp);
209         rtc.system_timestamp = cur_systime;
210         
211         if ( time_diff > 0 )
212         {
213            int          seconds;
214            int          minutes;
215            int          hours;
216            int          days;
217            int          month;
218            int          year;
219            int          temp_days;
220
221            int          year_hundreds;
222            int          year_tens;
223            int          year_ones;
224
225
226            if ( time_diff > DAYTICKS )
227            {
228                days = time_diff / DAYTICKS;
229                time_diff = time_diff - days * DAYTICKS;
230            }
231            else
232            {
233                days = 0;
234            }
235
236            if ( time_diff > HOURTICKS )
237            {
238                hours = time_diff / HOURTICKS;
239                time_diff = time_diff - hours * HOURTICKS;
240            }
241            else
242            {
243                hours = 0;
244            }
245
246            if ( time_diff > MINUTETICKS )
247            {
248                minutes = time_diff / MINUTETICKS;
249                time_diff = time_diff - minutes * MINUTETICKS;
250            }
251            else
252            {
253                minutes = 0;
254            }
255
256            if ( time_diff > 0 )
257            {
258                seconds = time_diff;
259            }
260            else
261            {
262                seconds = 0;
263            }
264
265
266            seconds += (rtc.data[1]*10 + rtc.data[0]);
267            if ( seconds >= 60 )
268            {
269                seconds -= 60;
270                minutes += 1;
271            }
272
273            minutes += (rtc.data[3]*10 + rtc.data[2]);
274            if ( minutes >= 60 )
275            {
276                minutes -= 60;
277                hours += 1;
278            }
279
280            hours += (rtc.data[5]*10 + rtc.data[4]);
281            if ( hours >= 24 )
282            {
283                hours -= 24;
284                days += 1;
285            }
286
287            if ( days > 0 )
288            {
289                year =  rtc.data[10]*10 + rtc.data[9];
290                year += ( 1000 + rtc.data[11] * 100 );
291
292                month = rtc.data[8];
293                days += (rtc.data[7]*10 + rtc.data[6]);
294                while ( days > (temp_days = S9xSRTCDaysInMmonth( month, year )) )
295                {
296                     days -= temp_days;
297                     month += 1;
298                     if ( month > 12 )
299                     {
300                         year += 1;
301                         month = 1;
302                     }
303                }
304
305                year_tens = year % 100;
306                year_ones = year_tens % 10;
307                year_tens /= 10;
308                year_hundreds = (year - 1000) / 100;
309
310                rtc.data[6] = days % 10;
311                rtc.data[7] = days / 10;
312                rtc.data[8] = month;
313                rtc.data[9] = year_ones;
314                rtc.data[10] = year_tens;
315                rtc.data[11] = year_hundreds;
316                rtc.data[12] = S9xSRTCComputeDayOfWeek ();
317            }
318
319            rtc.data[0] = seconds % 10;
320            rtc.data[1] = seconds / 10;
321            rtc.data[2] = minutes % 10;
322            rtc.data[3] = minutes / 10;
323            rtc.data[4] = hours % 10;
324            rtc.data[5] = hours / 10;
325
326            return;
327         }
328     }
329 }
330
331
332 /**********************************************************************************************/
333 /* S9xSetSRTC()                                                                               */
334 /* This function sends data to the S-RTC used in Dai Kaijyu Monogatari II                     */
335 /**********************************************************************************************/
336 void S9xSetSRTC (uint8 data, uint16 Address)
337 {
338
339     data &= 0x0F;       // Data is only 4-bits, mask out unused bits.
340
341     if( data >= 0xD )
342     {
343         // It's an RTC command
344
345         switch ( data )
346         {
347             case 0xD:
348                 rtc.mode = MODE_READ;
349                 rtc.index = -1;
350                 break;
351
352             case 0xE:
353                 rtc.mode = MODE_COMMAND;
354                 break;
355
356             default:
357                 // Ignore the write if it's an 0xF ???
358                 // Probably should switch back to read mode -- but this
359                 //  sequence never occurs in DKJM2
360                 break;
361         }
362
363         return;
364     }
365
366     if ( rtc.mode == MODE_LOAD_RTC )
367     {
368         if ( (rtc.index >= 0) || (rtc.index < MAX_RTC_INDEX) )
369         {
370             rtc.data[rtc.index++] = data;
371
372             if ( rtc.index == MAX_RTC_INDEX )
373             {
374                 // We have all the data for the RTC load
375
376                 rtc.system_timestamp = time (NULL);     // Get local system time
377
378                 // Get the day of the week
379                 rtc.data[rtc.index++] = S9xSRTCComputeDayOfWeek ();
380
381                 // Start RTC counting again
382                 rtc.count_enable = TRUE;
383                 rtc.needs_init = FALSE;
384             }
385
386             return;
387         }
388         else
389         {
390             // Attempting to write too much data
391             // error(); // ignore??
392         }
393     }
394     else if ( rtc.mode == MODE_COMMAND )
395     {
396         switch( data )
397         {
398             case COMMAND_CLEAR_RTC:
399                 // Disable RTC counter
400                 rtc.count_enable = FALSE;
401
402                 ZeroMemory (rtc.data, MAX_RTC_INDEX+1);
403                 rtc.index = -1;
404                 rtc.mode = MODE_COMMAND_DONE;
405                 break;
406
407             case COMMAND_LOAD_RTC:
408                 // Disable RTC counter
409                 rtc.count_enable = FALSE;
410
411                 rtc.index = 0;  // Setup for writing
412                 rtc.mode = MODE_LOAD_RTC;
413                 break;
414
415             default:
416                 rtc.mode = MODE_COMMAND_DONE;
417                 // unrecognized command - need to implement.
418         }
419
420         return;
421     }
422     else
423     {
424         if ( rtc.mode == MODE_READ )
425         {
426             // Attempting to write while in read mode. Ignore.
427         }
428
429         if ( rtc.mode == MODE_COMMAND_DONE )
430         {
431             // Maybe this isn't an error.  Maybe we should kick off
432             // a new E command.  But is this valid?
433         }
434     }
435 }
436
437 /**********************************************************************************************/
438 /* S9xGetSRTC()                                                                               */
439 /* This function retrieves data from the S-RTC                                                */
440 /**********************************************************************************************/
441 uint8 S9xGetSRTC (uint16 Address)
442 {
443     if ( rtc.mode == MODE_READ )
444     {
445         if ( rtc.index < 0 )
446         {
447             S9xUpdateSrtcTime ();       // Only update it if the game reads it
448             rtc.index++;
449             return ( 0x0f );        // Send start marker.
450         }
451         else if (rtc.index > MAX_RTC_INDEX)
452         {
453             rtc.index = -1;         // Setup for next set of reads
454             return ( 0x0f );        // Data done marker.
455         }
456         else
457         {
458             // Feed out the data
459             return rtc.data[rtc.index++];
460         }
461      }
462      else
463      {
464          return 0x0;
465      }
466 }
467
468 void S9xSRTCPreSaveState ()
469 {
470     if (Settings.SRTC)
471     {
472         S9xUpdateSrtcTime ();
473
474         int s = Memory.SRAMSize ?
475                 (1 << (Memory.SRAMSize + 3)) * 128 : 0;
476         if (s > 0x20000)
477             s = 0x20000;
478
479         SRAM [s + 0] = rtc.needs_init;
480         SRAM [s + 1] = rtc.count_enable;
481         memmove (&SRAM [s + 2], rtc.data, MAX_RTC_INDEX + 1);
482         SRAM [s + 3 + MAX_RTC_INDEX] = rtc.index;
483         SRAM [s + 4 + MAX_RTC_INDEX] = rtc.mode;
484
485 #ifdef LSB_FIRST
486         memmove (&SRAM [s + 5 + MAX_RTC_INDEX], &rtc.system_timestamp, 8);
487 #else
488         SRAM [s + 5  + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >>  0);
489         SRAM [s + 6  + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >>  8);
490         SRAM [s + 7  + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 16);
491         SRAM [s + 8  + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 24);
492         SRAM [s + 9  + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 32);
493         SRAM [s + 10 + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 40);
494         SRAM [s + 11 + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 48);
495         SRAM [s + 12 + MAX_RTC_INDEX] = (uint8) (rtc.system_timestamp >> 56);
496 #endif
497     }
498 }
499
500 void S9xSRTCPostLoadState ()
501 {
502     if (Settings.SRTC)
503     {
504         int s = Memory.SRAMSize ?
505                 (1 << (Memory.SRAMSize + 3)) * 128 : 0;
506         if (s > 0x20000)
507             s = 0x20000;
508
509         rtc.needs_init = SRAM [s + 0];
510         rtc.count_enable = SRAM [s + 1];
511         memmove (rtc.data, &SRAM [s + 2], MAX_RTC_INDEX + 1);
512         rtc.index = SRAM [s + 3 + MAX_RTC_INDEX];
513         rtc.mode = SRAM [s + 4 + MAX_RTC_INDEX];
514
515 #ifdef LSB_FIRST
516         memmove (&rtc.system_timestamp, &SRAM [s + 5 + MAX_RTC_INDEX], 8);
517 #else
518         rtc.system_timestamp |= (SRAM [s +  5 + MAX_RTC_INDEX] <<  0);
519         rtc.system_timestamp |= (SRAM [s +  6 + MAX_RTC_INDEX] <<  8);
520         rtc.system_timestamp |= (SRAM [s +  7 + MAX_RTC_INDEX] << 16);
521         rtc.system_timestamp |= (SRAM [s +  8 + MAX_RTC_INDEX] << 24);
522         rtc.system_timestamp |= (SRAM [s +  9 + MAX_RTC_INDEX] << 32);
523         rtc.system_timestamp |= (SRAM [s + 10 + MAX_RTC_INDEX] << 40);
524         rtc.system_timestamp |= (SRAM [s + 11 + MAX_RTC_INDEX] << 48);
525         rtc.system_timestamp |= (SRAM [s + 12 + MAX_RTC_INDEX] << 56);
526 #endif
527         S9xUpdateSrtcTime ();
528     }
529 }