2014年12月23日 星期二

[C/C++] A Clock Program by Reading CMOS RAM

The sample code as shown in the below. Please note that the syntax of inline assembly are based on the compiler which you used. This sample code is developed using Watcom and I have complied and executed this sample under Window XP. If you want to compile this sample by using GCC or VC compiler, please check their syntax of inline assembly first (you also can write your assembly into a module such that it can be called in your .c flile. However, you must use assembler to compile you assembly module and link it with your object file manually).
This sample read and show the system date and time from the CMOS. The program will terminate when user press any key from the key board.

timer.h

#ifndef TIMER_H
#define TIMER_H

#define SecIndex              0
#define AlarmSecIndex         1
#define MinIndex              2
#define AlarmMinIndex         3
#define HourIndex             4
#define AlarmHourIndex        5
#define DayIndex              7
#define MonthIndex            8
#define YearIndex             9

struct Time
{
    unsigned char sec;
    unsigned char min;
    unsigned char hr;
};

struct Date
{
    unsigned long year;
    unsigned char month;
    unsigned char day;
};

void readTime(struct Time *getTime);
void readDate(struct Date *getDate);
unsigned char readCmos(unsigned char _cmosIndex);
unsigned char BCDtoInt(unsigned char BCDNum);
void wait();

#endif
timer.c

#include "timer.h"

void readTime(struct Time *getTime)
{
    getTime->sec = readCmos(SecIndex);
    getTime->min = readCmos(MinIndex);
    getTime->hr = readCmos(HourIndex);
}

void readDate(struct Date *getDate)
{
    getDate->year = readCmos(YearIndex);
    getDate->month = readCmos(MonthIndex);
    getDate->day = readCmos(DayIndex);
}

unsigned char readCmos(unsigned char _cmosIndex)
{
    unsigned char _getByte,UIP;
    UIP = 1;

    /*
    Status Register A (Hex 00A)
    +--------------------------------------------------------+
    |  7   |  6   |   5   |   4   |  3  |  2  |   1  |   0   |
    |------+--------------+-------+--------------------------|
    | UIP  |    SDIV      |  BC   |            Rate          |
    +--------------------------------------------------------+
     UIP:     Update in progress
    SDIV:    Select divider
     BC:      Bank control
    */
    /*
    When the UIP bit is 1, an update is in progress.
    When it is set to 0, the current date and time can be read.
    */
    while(UIP)
    {
        _asm
        {
            mov  al, 0ah
            out  70h, al
            out  0edh, al ;delay a moment to avoid the error.
            in   al, 71h
            test al, 80h
            jz   Set
            jmp  Exit

         Set:
            mov  UIP, 0

         Exit:
        };
    }
    _asm
    {
         mov al, _cmosIndex
         out 70h, al ;output the index to the index port.
         out 0edh, al ;delay a moment to avoid the error.
         in al, 71h ;read the data from the cmos data port.
         mov _getByte, al
    };
    return _getByte;
}

void writeCmos(unsigned char _cmosIndex,unsigned char _byteData)
{
    _asm
    {
        mov al, _cmosIndex
        out 70h, al ;output the index to the index port.
        out 0edh, al ;this is used to delay a moment to avoid the error.
        mov al, _byteData
        out 71h, al ;write the data to the cmos through the data port.
    }
}

unsigned char BCDtoInt(unsigned char BCDNum)
{
    unsigned char intNum;
    intNum = ((BCDNum >> 4) * 10) + (BCDNum & 0x0f);
    return intNum;
}

unsigned char intToBCD(unsigned char intNum)
{
    unsigned BCDNum;
    BCDNum = ((intNum / 10) << 4) + (intNum % 10);
    return BCDNum;
}

void wait()
{
        struct Time currentTime, nextTime;
        readTime(&currentTime);
        readTime(&nextTime);
        while(nextTime.sec == currentTime.sec)
            readTime(&nextTime);
}
main.c

#include "timer.h"
#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct Time cmosTime;
    struct Date cmosDate;
    char _keyin = 0;
    
    readDate(&cmosDate);
    readTime(&cmosTime);
    printf("20%02d/%02d/%02d %02d:%02d:%02d\n",\
    BCDtoInt(cmosDate.year),BCDtoInt(cmosDate.month),BCDtoInt(cmosDate.day),BCDtoInt(cmosTime.hr),\
    BCDtoInt(cmosTime.min),BCDtoInt(cmosTime.sec));
    while(1)
    {
        wait();
        system("cls");
        readDate(&cmosDate);
        readTime(&cmosTime);
        printf("CMOS Time: 20%02d/%02d/%02d %02d:%02d:%02d\n",\
        BCDtoInt(cmosDate.year),BCDtoInt(cmosDate.month),BCDtoInt(cmosDate.day),BCDtoInt(cmosTime.hr),\
        BCDtoInt(cmosTime.min),BCDtoInt(cmosTime.sec));
        printf("Press Any Key to Exit\n");
        //detect keyboard
        _asm
        {
            mov  ah, 06h
            mov  dl, -1
            int  21h; maybe this interrupt will not available in the future.  
            mov  _keyin, al
        }
        if(_keyin)
            break;
    }
    return 0;
}

Fig.1 Execution Result

Note: you can read CMOS through 70,71,72 and 73 (72, 73's content may same as 70, 71's, it's depend on  your system BIOS) port.

Reference:
1. Open Watcom Download
2. Open Watcom C/C++ User's Guide
3. CMOS Registers

沒有留言:

張貼留言