DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/cmos.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <time.h>
00021 #include <math.h>
00022 
00023 #include "dosbox.h"
00024 #include "timer.h"
00025 #include "cpu.h"
00026 #include "pic.h"
00027 #include "inout.h"
00028 #include "mem.h"
00029 #include "bios_disk.h"
00030 #include "setup.h"
00031 #include "cross.h" //fmod on certain platforms
00032 #include "control.h"
00033 bool date_host_forced=false;
00034 #if defined (WIN32) && !defined (__MINGW32__)
00035 #include "sys/timeb.h"
00036 #else
00037 #include "sys/time.h"
00038 #endif
00039 
00040 // sigh... Windows doesn't know gettimeofday
00041 #if defined (WIN32) && !defined (__MINGW32__)
00042 typedef Bitu suseconds_t;
00043 
00044 struct timeval {
00045     time_t tv_sec;
00046     suseconds_t tv_usec;
00047 };
00048 
00049 static void gettimeofday (timeval* ptime, void* pdummy) {
00050     struct _timeb thetime;
00051     _ftime(&thetime);
00052 
00053     ptime->tv_sec = thetime.time;
00054     ptime->tv_usec = (Bitu)thetime.millitm;
00055 }
00056 
00057 #endif
00058 
00059 static struct {
00060     Bit8u regs[0x40];
00061     bool nmi;
00062     bool bcd;
00063     bool ampm;                  // am/pm mode (false = 24h mode)
00064     bool lock;                  // lock bit set (no updates)
00065     Bit8u reg;
00066     struct {
00067         bool enabled;
00068         Bit8u div;
00069         float delay;
00070         bool acknowledged;
00071     } timer;
00072     struct {
00073         double timer;
00074         double ended;
00075         double alarm;
00076     } last;
00077     bool update_ended;
00078     time_t time_diff;           // difference between real UTC and DOSbox UTC
00079     struct timeval locktime;    // UTC time of setting lock bit
00080     struct timeval safetime;    // UTC time of last safe time
00081 } cmos;
00082 
00083 static void cmos_timerevent(Bitu val) {
00084     (void)val;//UNUSED
00085     if (cmos.timer.acknowledged) {
00086         cmos.timer.acknowledged=false;
00087         PIC_ActivateIRQ(8);
00088     }
00089     if (cmos.timer.enabled) {
00090         PIC_AddEvent(cmos_timerevent,cmos.timer.delay);
00091         cmos.regs[0xc] = 0xC0;//Contraption Zack (music)
00092     }
00093 }
00094 
00095 static void cmos_checktimer(void) {
00096     PIC_RemoveEvents(cmos_timerevent);  
00097     if (cmos.timer.div<=2) cmos.timer.div+=7;
00098     cmos.timer.delay=(1000.0f/(32768.0f / (1 << (cmos.timer.div - 1))));
00099     if (!cmos.timer.div || !cmos.timer.enabled) return;
00100     LOG(LOG_PIT,LOG_NORMAL)("RTC Timer at %.2f hz",1000.0/cmos.timer.delay);
00101 //  PIC_AddEvent(cmos_timerevent,cmos.timer.delay);
00102     /* A rtc is always running */
00103     double remd=fmod(PIC_FullIndex(),(double)cmos.timer.delay);
00104     PIC_AddEvent(cmos_timerevent,(float)((double)cmos.timer.delay-remd)); //Should be more like a real pc. Check
00105 //  status reg A reading with this (and with other delays actually)
00106 }
00107 
00108 void cmos_selreg(Bitu port,Bitu val,Bitu iolen) {
00109     (void)port;//UNUSED
00110     (void)iolen;//UNUSED
00111     if (machine != MCH_PCJR) {
00112         /* bit 7 also controls NMI masking, if set, NMI is disabled */
00113         CPU_NMI_gate = (val&0x80) ? false : true;
00114     }
00115 
00116     cmos.reg=val & 0x3f;
00117     cmos.nmi=(val & 0x80)>0;
00118 }
00119 
00120 static void cmos_writereg(Bitu port,Bitu val,Bitu iolen) {
00121     (void)port;//UNUSED
00122     (void)iolen;//UNUSED
00123     if (date_host_forced && (cmos.reg <= 0x09 || cmos.reg == 0x32)) {   // date/time related registers
00124         if (cmos.bcd)           // values supplied are BCD, convert to binary values
00125         {
00126             if ((val & 0xf0) > 0x90 || (val & 0x0f) > 0x09) return;     // invalid BCD value
00127             // other checks for valid values are done in case-switch
00128 
00129             // convert pm hours differently (bcd 81-92 corresponds to hex 81-8c)
00130             if (cmos.reg == 0x04 && val >= 0x80)
00131             {
00132                 val = (val < 90) ? 0x80 : 0x8a + (val & 0x0f);
00133             }
00134             else
00135             {
00136                 val = ((val >> 4) * 10) + (val & 0x0f);
00137             }
00138         }
00139 
00140         struct tm *loctime;         // local dosbox time (based on dosbox UTC)
00141 
00142         if (cmos.lock)              // if locked, use locktime instead of current time
00143         {
00144             loctime = localtime((time_t*)&cmos.locktime.tv_sec);
00145         }
00146         else                        // not locked, use current time
00147         {
00148             struct timeval curtime;
00149             gettimeofday(&curtime, NULL);
00150             curtime.tv_sec += cmos.time_diff;
00151             loctime = localtime((time_t*)&curtime.tv_sec);
00152         }
00153 
00154         switch (cmos.reg)
00155         {
00156         case 0x00:      /* Seconds */
00157             if (val > 59) return;       // invalid seconds value
00158             loctime->tm_sec = (int)val;
00159             break;
00160 
00161         case 0x02:      /* Minutes */
00162             if (val > 59) return;       // invalid minutes value
00163             loctime->tm_min = (int)val;
00164             break;
00165 
00166         case 0x04:      /* Hours */
00167             if (cmos.ampm)              // 12h am/pm mode
00168             {
00169                 if ((val > 12 && val < 0x81) || val > 0x8c) return; // invalid hour value
00170                 if (val > 12) val -= (0x80-12);         // convert pm to 24h
00171             }
00172             else                        // 24h mode
00173             {
00174                 if (val > 23) return;                               // invalid hour value
00175             }
00176 
00177             loctime->tm_hour = (int)val;         
00178             break;
00179 
00180         case 0x06:      /* Day of week */
00181             // seems silly to set this, as it is calculated? ignore for now
00182             break;
00183 
00184         case 0x07:      /* Date of month */
00185             if (val > 31) return;               // invalid date value (mktime() should catch the rest)
00186             loctime->tm_mday = (int)val;
00187             break;
00188 
00189         case 0x08:      /* Month */
00190             if (val > 12) return;               // invalid month value
00191             loctime->tm_mon = (int)val;
00192             break;
00193 
00194         case 0x09:      /* Year */
00195             loctime->tm_year = (int)val;
00196             break;
00197 
00198         case 0x32:      /* Century */
00199             if (val < 19) return;               // invalid century value?
00200             loctime->tm_year += (int)((val * 100) - 1900);
00201             break;
00202 
00203         case 0x01:      /* Seconds Alarm */
00204         case 0x03:      /* Minutes Alarm */
00205         case 0x05:      /* Hours Alarm */
00206             LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Trying to set alarm");
00207             cmos.regs[cmos.reg] = (Bit8u)val;
00208             return;     // done
00209         }
00210 
00211         time_t newtime = mktime(loctime);       // convert new local time back to dosbox UTC
00212 
00213         if (newtime != (time_t)-1)
00214         {
00215             if (!cmos.lock)         // no lock, takes immediate effect
00216             {
00217                 cmos.time_diff = newtime - time(NULL);  // calculate new diff
00218             }
00219             else
00220             {
00221                 cmos.locktime.tv_sec = newtime;         // store for later use
00222                 // no need to set usec, we don't use it
00223             }
00224         }
00225 
00226         return;
00227     }
00228 
00229     switch (cmos.reg) {
00230     case 0x00:      /* Seconds */
00231     case 0x02:      /* Minutes */
00232     case 0x04:      /* Hours */
00233     case 0x06:      /* Day of week */
00234     case 0x07:      /* Date of month */
00235     case 0x08:      /* Month */
00236     case 0x09:      /* Year */
00237     case 0x32:              /* Century */
00238         /* Ignore writes to change alarm */
00239         if(!date_host_forced) break;
00240     case 0x01:      /* Seconds Alarm */
00241     case 0x03:      /* Minutes Alarm */
00242     case 0x05:      /* Hours Alarm */
00243         if(!date_host_forced) {
00244             LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Trying to set alarm");
00245             cmos.regs[cmos.reg]=(Bit8u)val;
00246             break;
00247         }
00248     case 0x0a:      /* Status reg A */
00249         cmos.regs[cmos.reg]=val & 0x7f;
00250         if ((val & 0x70)!=0x20) LOG(LOG_BIOS,LOG_ERROR)("CMOS Illegal 22 stage divider value");
00251         cmos.timer.div=(val & 0xf);
00252         cmos_checktimer();
00253         break;
00254     case 0x0b:      /* Status reg B */
00255         if(date_host_forced) {
00256             bool waslocked = cmos.lock;
00257 
00258             cmos.ampm = !(val & 0x02);
00259             cmos.bcd = !(val & 0x04);
00260             if ((val & 0x10) != 0) LOG(LOG_BIOS,LOG_ERROR)("CMOS:Updated ended interrupt not supported yet");
00261             cmos.timer.enabled = (val & 0x40) > 0;
00262             cmos.lock = (val & 0x80) != 0;
00263 
00264             if (cmos.lock)              // if locked, set locktime for later use
00265             {
00266                 if (!waslocked)         // if already locked, no further action
00267                 {
00268                     // locked for the first time, calculate dosbox UTC
00269                     gettimeofday(&cmos.locktime, NULL);
00270                     cmos.locktime.tv_sec += cmos.time_diff;
00271                 }
00272             }
00273             else if (waslocked)         // time was locked, now unlock
00274             {
00275                 // calculate new diff between real UTC and dosbox UTC
00276                 cmos.time_diff = cmos.locktime.tv_sec - time(NULL);
00277             }
00278 
00279             cmos.regs[cmos.reg] = (Bit8u)val;
00280             cmos_checktimer();
00281         } else {
00282             cmos.bcd=!(val & 0x4);
00283             cmos.regs[cmos.reg]=val & 0x7f;
00284             cmos.timer.enabled=(val & 0x40)>0;
00285             if (val&0x10) LOG(LOG_BIOS,LOG_ERROR)("CMOS:Updated ended interrupt not supported yet");
00286             cmos_checktimer();
00287         }
00288         break;
00289     case 0x0c:
00290         if(date_host_forced) break;
00291     case 0x0d:/* Status reg D */
00292         if(!date_host_forced) {
00293             cmos.regs[cmos.reg]=val & 0x80; /*Bit 7=1:RTC Pown on*/
00294         }
00295         break;
00296     case 0x0f:      /* Shutdown status byte */
00297         cmos.regs[cmos.reg]=val & 0x7f;
00298         break;
00299     default:
00300         cmos.regs[cmos.reg]=val & 0x7f;
00301         LOG(LOG_BIOS,LOG_ERROR)("CMOS:WRite to unhandled register %x",cmos.reg);
00302     }
00303 }
00304 
00305 unsigned char CMOS_GetShutdownByte() {
00306     return cmos.regs[0x0F];
00307 }
00308 
00309 #define MAKE_RETURN(_VAL) ((unsigned char)(cmos.bcd ? (((((unsigned int)_VAL) / 10U) << 4U) | (((unsigned int)_VAL) % 10U)) : ((unsigned int)_VAL)))
00310 
00311 static Bitu cmos_readreg(Bitu port,Bitu iolen) {
00312     (void)port;//UNUSED
00313     (void)iolen;//UNUSED
00314     if (cmos.reg>0x3f) {
00315         LOG(LOG_BIOS,LOG_ERROR)("CMOS:Read from illegal register %x",cmos.reg);
00316         return 0xff;
00317     }
00318 
00319     // JAL_20060817 - rewrote most of the date/time part
00320     if (date_host_forced && (cmos.reg <= 0x09 || cmos.reg == 0x32)) {       // date/time related registers
00321         struct tm* loctime;
00322 
00323         if (cmos.lock)              // if locked, use locktime instead of current time
00324         {
00325             loctime = localtime((time_t*)&cmos.locktime.tv_sec);
00326         }
00327         else                        // not locked, get current time
00328         {
00329             struct timeval curtime;
00330             gettimeofday(&curtime, NULL);
00331     
00332             // allow a little more leeway (1 sec) than the .244 sec officially given
00333             if (curtime.tv_sec - cmos.safetime.tv_sec == 1 &&
00334                 curtime.tv_usec < cmos.safetime.tv_usec)
00335             {
00336                 curtime = cmos.safetime;        // within safe range, use safetime instead of current time
00337             }
00338 
00339             curtime.tv_sec += cmos.time_diff;
00340             loctime = localtime((time_t*)&curtime.tv_sec);
00341         }
00342 
00343         switch (cmos.reg)
00344         {
00345         case 0x00:      // seconds
00346             return MAKE_RETURN(loctime->tm_sec);
00347         case 0x02:      // minutes
00348             return MAKE_RETURN(loctime->tm_min);
00349         case 0x04:      // hours
00350             if (cmos.ampm && loctime->tm_hour > 12)     // time pm, convert
00351             {
00352                 loctime->tm_hour -= 12;
00353                 loctime->tm_hour += (cmos.bcd) ? 80 : 0x80;
00354             }
00355             return MAKE_RETURN(loctime->tm_hour);
00356         case 0x06:      /* Day of week */
00357             return MAKE_RETURN(loctime->tm_wday + 1);
00358         case 0x07:      /* Date of month */
00359             return MAKE_RETURN(loctime->tm_mday);
00360         case 0x08:      /* Month */
00361             return MAKE_RETURN(loctime->tm_mon + 1);
00362         case 0x09:      /* Year */
00363             return MAKE_RETURN(loctime->tm_year % 100);
00364         case 0x32:      /* Century */
00365             return MAKE_RETURN(loctime->tm_year / 100 + 19);
00366 
00367         case 0x01:      /* Seconds Alarm */
00368         case 0x03:      /* Minutes Alarm */
00369         case 0x05:      /* Hours Alarm */
00370             return MAKE_RETURN(cmos.regs[cmos.reg]);
00371         }
00372     }
00373 
00374     Bitu drive_a, drive_b;
00375     Bit8u hdparm;
00376     time_t curtime;
00377     struct tm *loctime;
00378     /* Get the current time. */
00379     curtime = time (NULL);
00380 
00381     /* Convert it to local time representation. */
00382     loctime = localtime (&curtime);
00383 
00384     switch (cmos.reg) {
00385     case 0x00:      /* Seconds */
00386         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_sec);
00387     case 0x02:      /* Minutes */
00388         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_min);
00389     case 0x04:      /* Hours */
00390         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_hour);
00391     case 0x06:      /* Day of week */
00392         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_wday + 1);
00393     case 0x07:      /* Date of month */
00394         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_mday);
00395     case 0x08:      /* Month */
00396         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_mon + 1);
00397     case 0x09:      /* Year */
00398         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_year % 100);
00399     case 0x32:      /* Century */
00400         if(!date_host_forced) return    MAKE_RETURN(loctime->tm_year / 100 + 19);
00401     case 0x01:      /* Seconds Alarm */
00402     case 0x03:      /* Minutes Alarm */
00403     case 0x05:      /* Hours Alarm */
00404         if(!date_host_forced) return cmos.regs[cmos.reg];
00405     case 0x0a:      /* Status register A */
00406         if(date_host_forced) {
00407             // take bit 7 of reg b into account (if set, never updates)
00408             gettimeofday (&cmos.safetime, NULL);        // get current UTC time
00409             if (cmos.lock ||                            // if lock then never updated, so reading safe
00410                 cmos.safetime.tv_usec < (1000-244)) {   // if 0, at least 244 usec should be available
00411                 return cmos.regs[0x0a];                 // reading safe
00412             } else {
00413                 return cmos.regs[0x0a] | 0x80;          // reading not safe!
00414             }
00415         } else {
00416             if (PIC_TickIndex()<0.002) {
00417                 return (cmos.regs[0x0a]&0x7f) | 0x80;
00418             } else {
00419                 return (cmos.regs[0x0a]&0x7f);
00420             }
00421         }
00422     case 0x0c:      /* Status register C */
00423         cmos.timer.acknowledged=true;
00424         if (cmos.timer.enabled) {
00425             /* In periodic interrupt mode only care for those flags */
00426             Bit8u val=cmos.regs[0xc];
00427             cmos.regs[0xc]=0;
00428             return val;
00429         } else {
00430             /* Give correct values at certain times */
00431             Bit8u val=0;
00432             double index=PIC_FullIndex();
00433             if (index>=(cmos.last.timer+cmos.timer.delay)) {
00434                 cmos.last.timer=index;
00435                 val|=0x40;
00436             } 
00437             if (index>=(cmos.last.ended+1000)) {
00438                 cmos.last.ended=index;
00439                 val|=0x10;
00440             }
00441             if(date_host_forced) cmos.regs[0xc] = 0;        // JAL_20060817 - reset here too!
00442             return val;
00443         }
00444     case 0x10:      /* Floppy size */
00445         drive_a = 0;
00446         drive_b = 0;
00447         if(imageDiskList[0] != NULL) drive_a = imageDiskList[0]->GetBiosType();
00448         if(imageDiskList[1] != NULL) drive_b = imageDiskList[1]->GetBiosType();
00449         return ((drive_a << 4) | (drive_b));
00450     /* First harddrive info */
00451     case 0x12:
00452         /* NTS: DOSBox 0.74 mainline has these backwards: the upper nibble is the first hard disk,
00453            the lower nibble is the second hard disk. It makes a big difference to stupid OS's like
00454            Windows 95. */
00455         hdparm = 0;
00456         if(imageDiskList[3] != NULL) hdparm |= 0xf;
00457         if(imageDiskList[2] != NULL) hdparm |= 0xf0;
00458 //      hdparm = 0;
00459         return hdparm;
00460     case 0x19:
00461         if(imageDiskList[2] != NULL) return 47; /* User defined type */
00462         return 0;
00463     case 0x1b:
00464         if(imageDiskList[2] != NULL) return (imageDiskList[2]->cylinders & 0xff);
00465         return 0;
00466     case 0x1c:
00467         if(imageDiskList[2] != NULL) return ((imageDiskList[2]->cylinders & 0xff00)>>8);
00468         return 0;
00469     case 0x1d:
00470         if(imageDiskList[2] != NULL) return (imageDiskList[2]->heads);
00471         return 0;
00472     case 0x1e:
00473         if(imageDiskList[2] != NULL) return 0xff;
00474         return 0;
00475     case 0x1f:
00476         if(imageDiskList[2] != NULL) return 0xff;
00477         return 0;
00478     case 0x20:
00479         if(imageDiskList[2] != NULL) return (0xc0 | (((imageDiskList[2]->heads) > 8) << 3));
00480         return 0;
00481     case 0x21:
00482         if(imageDiskList[2] != NULL) return (imageDiskList[2]->cylinders & 0xff);
00483         return 0;
00484     case 0x22:
00485         if(imageDiskList[2] != NULL) return ((imageDiskList[2]->cylinders & 0xff00)>>8);
00486         return 0;
00487     case 0x23:
00488         if(imageDiskList[2] != NULL) return (imageDiskList[2]->sectors);
00489         return 0;
00490     /* Second harddrive info */
00491     case 0x1a:
00492         if(imageDiskList[3] != NULL) return 47; /* User defined type */
00493         return 0;
00494     case 0x24:
00495         if(imageDiskList[3] != NULL) return (imageDiskList[3]->cylinders & 0xff);
00496         return 0;
00497     case 0x25:
00498         if(imageDiskList[3] != NULL) return ((imageDiskList[3]->cylinders & 0xff00)>>8);
00499         return 0;
00500     case 0x26:
00501         if(imageDiskList[3] != NULL) return (imageDiskList[3]->heads);
00502         return 0;
00503     case 0x27:
00504         if(imageDiskList[3] != NULL) return 0xff;
00505         return 0;
00506     case 0x28:
00507         if(imageDiskList[3] != NULL) return 0xff;
00508         return 0;
00509     case 0x29:
00510         if(imageDiskList[3] != NULL) return (0xc0 | (((imageDiskList[3]->heads) > 8) << 3));
00511         return 0;
00512     case 0x2a:
00513         if(imageDiskList[3] != NULL) return (imageDiskList[3]->cylinders & 0xff);
00514         return 0;
00515     case 0x2b:
00516         if(imageDiskList[3] != NULL) return ((imageDiskList[3]->cylinders & 0xff00)>>8);
00517         return 0;
00518     case 0x2c:
00519         if(imageDiskList[3] != NULL) return (imageDiskList[3]->sectors);
00520         return 0;
00521     case 0x39:
00522         return 0;
00523     case 0x3a:
00524         return 0;
00525 
00526 
00527     case 0x0b:      /* Status register B */
00528     case 0x0d:      /* Status register D */
00529     case 0x0f:      /* Shutdown status byte */
00530     case 0x14:      /* Equipment */
00531     case 0x15:      /* Base Memory KB Low Byte */
00532     case 0x16:      /* Base Memory KB High Byte */
00533     case 0x17:      /* Extended memory in KB Low Byte */
00534     case 0x18:      /* Extended memory in KB High Byte */
00535     case 0x30:      /* Extended memory in KB Low Byte */
00536     case 0x31:      /* Extended memory in KB High Byte */
00537 //      LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Read from reg %X : %04X",cmos.reg,cmos.regs[cmos.reg]);
00538         return cmos.regs[cmos.reg];
00539     case 0x2F:
00540         extern bool PS1AudioCard;
00541         if( PS1AudioCard )
00542             return 0xFF;
00543     default:
00544         LOG(LOG_BIOS,LOG_NORMAL)("CMOS:Read from reg %X",cmos.reg);
00545         return cmos.regs[cmos.reg];
00546     }
00547 }
00548 
00549 void CMOS_SetRegister(Bitu regNr, Bit8u val) {
00550     cmos.regs[regNr] = val;
00551 }
00552 
00553 
00554 static IO_ReadHandleObject ReadHandler[2];
00555 static IO_WriteHandleObject WriteHandler[2];    
00556 
00557 void CMOS_Destroy(Section* sec) {
00558     (void)sec;//UNUSED
00559 }
00560 
00561 void CMOS_Reset(Section* sec) {
00562     (void)sec;//UNUSED
00563     LOG(LOG_MISC,LOG_DEBUG)("CMOS_Reset(): reinitializing CMOS/RTC controller");
00564 
00565     WriteHandler[0].Uninstall();
00566     WriteHandler[1].Uninstall();
00567     ReadHandler[0].Uninstall();
00568     ReadHandler[1].Uninstall();
00569 
00570     if (IS_PC98_ARCH)
00571         return;
00572 
00573     WriteHandler[0].Install(0x70,cmos_selreg,IO_MB);
00574     WriteHandler[1].Install(0x71,cmos_writereg,IO_MB);
00575     ReadHandler[0].Install(0x71,cmos_readreg,IO_MB);
00576     cmos.timer.enabled=false;
00577     cmos.timer.acknowledged=true;
00578     cmos.reg=0xa;
00579     cmos_writereg(0x71,0x26,1);
00580     cmos.reg=0xb;
00581     cmos_writereg(0x71,0x2,1);  //Struct tm *loctime is of 24 hour format,
00582     if(date_host_forced) {
00583         cmos.regs[0x0d]=(Bit8u)0x80;
00584     } else {
00585         cmos.reg=0xd;
00586         cmos_writereg(0x71,0x80,1); /* RTC power on */
00587     }
00588     // Equipment is updated from bios.cpp and bios_disk.cpp
00589     /* Fill in base memory size, it is 640K always */
00590     cmos.regs[0x15]=(Bit8u)0x80;
00591     cmos.regs[0x16]=(Bit8u)0x02;
00592     /* Fill in extended memory size */
00593     Bitu exsize=MEM_TotalPages()*4;
00594     if (exsize >= 1024) exsize -= 1024;
00595     else exsize = 0;
00596     if (exsize > 65535) exsize = 65535; /* cap at 64MB. this value is returned as-is by INT 15H AH=0x88 in a 16-bit register */
00597     cmos.regs[0x17]=(Bit8u)exsize;
00598     cmos.regs[0x18]=(Bit8u)(exsize >> 8);
00599     cmos.regs[0x30]=(Bit8u)exsize;
00600     cmos.regs[0x31]=(Bit8u)(exsize >> 8);
00601     if (date_host_forced) {
00602         cmos.time_diff = 0;
00603         cmos.locktime.tv_sec = 0;
00604     }
00605 }
00606 
00607 void CMOS_Init() {
00608     LOG(LOG_MISC,LOG_DEBUG)("Initializing CMOS/RTC");
00609 
00610     if (control->opt_date_host_forced) {
00611         LOG_MSG("Synchronize date with host: Forced");
00612         date_host_forced=true;
00613     }
00614 
00615     AddExitFunction(AddExitFunctionFuncPair(CMOS_Destroy),true);
00616     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(CMOS_Reset));
00617 }
00618 
00619 // save state support
00620 void *cmos_timerevent_PIC_Event = (void*)((uintptr_t)cmos_timerevent);
00621 
00622 namespace
00623 {
00624 class SerializeCmos : public SerializeGlobalPOD
00625 {
00626 public:
00627     SerializeCmos() : SerializeGlobalPOD("CMOS")
00628     {
00629         registerPOD(cmos.regs);
00630         registerPOD(cmos.nmi);
00631         registerPOD(cmos.reg);
00632         registerPOD(cmos.timer.enabled);
00633         registerPOD(cmos.timer.div);
00634         registerPOD(cmos.timer.delay);
00635         registerPOD(cmos.timer.acknowledged);
00636         registerPOD(cmos.last.timer);
00637         registerPOD(cmos.last.ended);
00638         registerPOD(cmos.last.alarm);
00639         registerPOD(cmos.update_ended);
00640     }
00641 } dummy;
00642 }