//----------------------------------------------------------------------------// // GNU GPL OS/K // // // // Desc: RTC Time related functions // // // // // // Copyright © 2018-2019 The OS/K Team // // // // This file is part of OS/K. // // // // OS/K is free software: you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation, either version 3 of the License, or // // any later version. // // // // OS/K is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY//without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License for more details. // // // // You should have received a copy of the GNU General Public License // // along with OS/K. If not, see . // //----------------------------------------------------------------------------// #include #include #include static ulong Ticks = 0; static Time_t OriginTime; static Time_t CurTime; // TODO asnprintf() static char TimeFmtBuf[22] = { 0 }; static uchar RTC_RATE = 0x05; //2048Hz static char time24or12Mode; static void GetTimeFromRTC(void) { Time_t lastTime; char updateInProgress = 1; // Wait while the RTC updates its value while (updateInProgress) { IoWriteByteOnPort(0x70, 0x0A); updateInProgress = (IoReadByteFromPort(0x71) & 0x80); } IoWriteByteOnPort(0x70, 0x0); OriginTime.sec = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x02); OriginTime.min = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x04); OriginTime.hour = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x06); OriginTime.weekday = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x07); OriginTime.day = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x08); OriginTime.month = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x09); OriginTime.year = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x32); OriginTime.century = IoReadByteFromPort(0x71); // Now while we don't get the same value, read the registers (ensure data are valid) do { lastTime.sec = OriginTime.sec; lastTime.min = OriginTime.min; lastTime.hour = OriginTime.hour; lastTime.weekday = OriginTime.weekday; lastTime.day = OriginTime.day; lastTime.month = OriginTime.month; lastTime.year = OriginTime.year; lastTime.century = OriginTime.century; while (updateInProgress) { IoWriteByteOnPort(0x70, 0x0A); updateInProgress = (IoReadByteFromPort(0x71) & 0x80); } IoWriteByteOnPort(0x70, 0x0); OriginTime.sec = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x02); OriginTime.min = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x04); OriginTime.hour = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x06); OriginTime.weekday = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x07); OriginTime.day = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x08); OriginTime.month = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x09); OriginTime.year = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x32); OriginTime.century = IoReadByteFromPort(0x71); } while ((lastTime.sec != OriginTime.sec) || (lastTime.min != OriginTime.min) || (lastTime.hour != OriginTime.hour) || (lastTime.weekday != OriginTime.weekday) || (lastTime.day != OriginTime.day) || (lastTime.month != OriginTime.month) || (lastTime.year != OriginTime.year) || (lastTime.century != OriginTime.century) ); IoWriteByteOnPort(0x70, 0x0B); time24or12Mode = IoReadByteFromPort(0x71); // Convert to binary if it is necessary if (!(time24or12Mode & 0x04)) { OriginTime.sec = (OriginTime.sec & 0x0F) + ((OriginTime.sec / 16) * 10); OriginTime.min = (OriginTime.min & 0x0F) + ((OriginTime.min / 16) * 10); OriginTime.hour = ( (OriginTime.hour & 0x0F) + (((OriginTime.hour & 0x70) / 16) * 10) ) | (OriginTime.hour & 0x80); OriginTime.day = (OriginTime.day & 0x0F) + ((OriginTime.day / 16) * 10); OriginTime.month = (OriginTime.month & 0x0F) + ((OriginTime.month / 16) * 10); OriginTime.year = (OriginTime.year & 0x0F) + ((OriginTime.year / 16) * 10); OriginTime.century = (OriginTime.century & 0x0F) + ((OriginTime.century / 16) * 10); OriginTime.weekday = (OriginTime.weekday & 0x0F) + ((OriginTime.weekday / 16) * 10); } // Convert 12 to 24 hour if necessary if (!(time24or12Mode & 0x02) && (OriginTime.hour & 0x80)) { OriginTime.hour = ((OriginTime.hour & 0x7)+ 10) % 24; } CurTime.sec = OriginTime.sec; CurTime.min = OriginTime.min; CurTime.hour = OriginTime.hour; CurTime.weekday = OriginTime.weekday; CurTime.day = OriginTime.day; CurTime.month = OriginTime.month; CurTime.year = OriginTime.year; CurTime.century = OriginTime.century; } // // ISR handler for the real time clock // static void HandleRTC(ISRFrame_t *regs) { IoWriteByteOnPort(0x70, 0x0C); // Selects status reg C IoReadByteFromPort(0x71); // Flush Ticks++; KeSendEOItoPIC(0x28); } char *KeFormatCurTime(void) { Time_t *RtcTime = KeGetCurTime(); snprintf(TimeFmtBuf, sizeof(TimeFmtBuf), "%02d/%02d/%04d - %02d:%02d:%02d", RtcTime->day, RtcTime->month, RtcTime->year + RtcTime->century*100, RtcTime->hour, RtcTime->min, RtcTime->sec ); return TimeFmtBuf; } static void UpdateCurTime(void) { ulong frequency = 32768 >> (RTC_RATE - 1); uchar minRemain, hourRemain, dayRemain; CurTime.sec = (uchar)(((ulong)OriginTime.sec + (Ticks / frequency)) % 60); minRemain = (uchar)(((ulong)OriginTime.sec + (Ticks / frequency)) / 60); CurTime.min = (uchar)(((ulong)OriginTime.min + minRemain) % 60); hourRemain = (uchar)(((ulong)OriginTime.min + minRemain) / 60); CurTime.hour = (uchar)(((ulong)OriginTime.hour + hourRemain) % 24); dayRemain = (uchar)(((ulong)OriginTime.hour + hourRemain) / 24); if (dayRemain) { KeStartPanic("[RTC] We must shutdown this computer for your safety.\n"); } } Time_t* KeGetCurTime(void) { UpdateCurTime(); return &CurTime; } static uint IsLeapYear(uint year) { if (!(year % 4)) { return 0; } return year % 100 == 0 ? (year % 400 == 0) : 1; } static uint DaysInMonth(uint month, uint year) { return (month == 2) ? (28 + IsLeapYear(year)) : 31 - (month - 1) % 7 % 2; } ulong KeGetTimeStamp(void) { Time_t *time = KeGetCurTime(); uint dpy = 365 + IsLeapYear(time->year); uint dim = DaysInMonth(time->month, time->year + time->century * 100); return time->sec + time->min * 60 + time->hour * 60 * 60 + time->day * 24 * 60 * 60 + time->month * dim * 24 * 60 * 60 + (time->year + time->century * 100) * dpy * 24 * 60 * 60; } ulong KeGetClockTicks(void) { return Ticks; } void KeEnableRTC(void) { ulong flags = KePauseIRQs(); char readInterruptConfig; char readRegister; char readIrqs; KeRegisterISR(HandleRTC, 0x28); // Setting up the register control and interrupt rates DebugLog("\tRTC interrupt frequency set to %d Hz\n", 32768 >> (RTC_RATE - 1)); IoWriteByteOnPort(0x70, 0x8B); readRegister = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x8B); // Because reading flushes IoWriteByteOnPort(0x71, readRegister | 0x40); IoWriteByteOnPort(0x70, 0x8A); readInterruptConfig = IoReadByteFromPort(0x71); IoWriteByteOnPort(0x70, 0x8A); // Because reading flushes IoWriteByteOnPort(0x71, (readInterruptConfig & 0xF0) | RTC_RATE); IoWriteByteOnPort(0x70, 0x0C); IoReadByteFromPort(0x71); // Flush // Setting up the IRQs readIrqs = IoReadByteFromPort(0xA1); IoWriteByteOnPort(0xA1, 0xFE & readIrqs); // Enables IRQ on PIC 2 readIrqs = IoReadByteFromPort(0x21); IoWriteByteOnPort(0x21, 0xFB & readIrqs); // Enables IRQ on PIC 1 // Clean-up IoWriteByteOnPort(0x70, 0x0C); // Select status reg C IoReadByteFromPort(0x71); // Flush GetTimeFromRTC(); KeRestoreIRQs(flags); KeEnableNMI(); srand(KeGetTimeStamp()); } void KeDelayExecution(uint time) { ulong frequency = 32768 >> (RTC_RATE - 1); ulong beginTick = KeGetClockTicks(); while (KeGetClockTicks() < beginTick + (frequency/1000) * time) { KeRelaxCPU(); } }