The following text and the code for this COM file were written by Ralph
Wyatt. He has given me permission to distribute it as FREEWARE. Call
this com program at the end of a BAT file that launches a game that
alters the computer clock without resetting it. LEMMINGS and Apogee's
DUKE NUKEM are 2 games that I use this file on.


This program "RESETCLK.COM" reads the current time and date from
 CMOS RAM in order to automatically restore the correct time of
 day in AT systems where the time has been lost or corrupted.

CMOS RAM is an acronym for Complimentary Metal Oxide
 Semiconductor Random Access Memory. It is an integral part of
 all AT class systems, but doesn't occupy any of the base 1Mb of
 address space. It is non-volatile and will retain its "memory"
 as long as the battery in the AT remains charged.

This "external" RAM is accessed through hardware ports 70H-7FH.
 However, only ports 70H and 71H are used for our purpose here.
 Information is written to or read from the CMOS RAM by first
 sending an address to port 70H. This "conditions" the MC-146818
 processor for the next event which can be either a read or a
 write operation at port 71H, depending on whether an IN or an
 OUT instruction is issued by the main CPU.

Writes to port 71H are just a little bit trickier than reads in
 that a little further "conditioning" of the MC-146818 processor
 is needed. In this program, we're only going to be reading from
 port 71H, so I'll not go into the necessary settings of the
 various status registers in the MC-146818 to accomplish writing
 to the CMOS RAM.

The addresses within the MC-146818 that we're interested in for
 this project are:

  Byte    Contents

   0       Current Second
   2       Current Minute
   4       Current Hour
   7       Day of Month
   8       Month of Year
   9       Year of Century

Another byte which could be of interest is byte 50 which
 contains the 2-digit century. For purposes of this program, I
 think it is safe to assume 1900 as the century, so we'll not
 access byte 50.

The data at these memory locations can be in either binary or
 packed Binary Coded Decimal (BCD) and the method used to store
 the data is available as a bit switch in one of the Status
 Registers within the Real Time Clock (RTC) which is an integral
 component of the Motorola MC-146818 Processor. During boot, the
 BIOS sets this switch so data are stored in packed BCD.

Packed BCD is where a 2-digit decimal number is stored in a
 single byte as a pair of binary integers with the leftmost four
 bits representing the most significant digit and the rightmost
 four bits are the least significant digit.

A byte containing the binary representation of the decimal value
 52 in packed BCD would look like: |0101+0010|
                                    5     2

A byte containing the straight binary value 52 would look like:
                                  |0011+0100|

As you can see, there's quite a difference. Part of our problem
 in this program is finding a way to convert that packed BCD data
 coming from the Motorola chip into standard binary data for the
 INTEL chip.


Now, let's take a look at the program code.

Lines 1 thru 8:   These are just the normal assembler directives
                  and the obligatory Jump instruction that is
                  part of almost all programs destined to become
                  .COM modules.

Lines 10 thru 16: Local storage to hold the completed (binary)
                  form of the data we read from the RTC.

Lines 18 thru 20: Here's a block of code we'll repeat several
                  times in the ensuing lines. First an address
                  value within the RTC is placed in AL. Then a
                  routine is called which will read the value at
                  that address and convert it from BCD to
                  binary. Here we get the current seconds.

Lines 22 thru 40: The above sequence is repeated for the
                  remainder of the data of interest from the
                  clock.

Lines 42 thru 49: Here we set up for and call DOS to set the
                  BIOS clock to the TIME retrieved from the RTC.
                  Notice that the hundredths of a second are
                  arbitrarily set to 50. It ain't right on, but
                  it's close.

Lines 51 thru 58: Here we do the same for the correct DATE. This
                  covers the case where midnight has passed
                  while the BIOS clock was inoperative. Note
                  that we add 1900 to the year as gotten from
                  the RTC. Further note that the year value
                  passed to DOS is a full 16-bit integer whilst
                  all the other values were 8-bit integers.

Lines 60 thru 62: Normal end-of-program return to DOS stuff.

Lines 64 thru 83: This is the only interesting part of the
                  program. Here's where the address of the data
                  to be read is passed to the RTC, the data is
                  read and finally converted to binary.

Lines 66 thru 72: The address which was passed to this routine
                  in AL is passed on to the RTC via the OUT
                  instruction to port 70H. A couple of do-
                  nothing JMP's are issued just to ensure that
                  the Motorola chip has sufficient time to
                  respond. Next the data from the RTC is read
                  into AL via an IN instruction to port 71H.
                  Again, we use a couple of dummy jumps to give
                  the port time to respond.

Lines 74 thru 80: Here the BCD digits in AL get converted to an
                  8-bit binary value. First, both the digits are
                  copied to AH. Then the rightmost 4 bits are
                  shifted out, leaving only the most significant
                  digit from AL. Next a mask is logically anded
                  with both registers and we wind up with a four
                  bit binary value in each register with AH and
                  AL respectively holding the tens and units
                  digits of our original BCD number. All that
                  remains is to multiply the tens digit by 10
                  and add it to the units digit. Here a little
                  creative use of the instruction set is made.
                  The ASCII Adjust after Division (AAD)
                  instruction seems tailor made for this
                  situation, though I doubt it was intended by
                  the designers at INTEL to do so. It does the
                  multiplication and addition just like we want
                  though, so we'll happily use it here. It
                  effectively multiplies AH by 10 and adds the
                  result to AL, then sets AH to zero.
                  Perfect! Just what we're looking for. Now, AL
                  contains the binary equivalent of the original
                  BCD data obtained from the RTC.



CSEG               SEGMENT PARA PUBLIC 'CODE'
                   ASSUME CS:CSEG
                   ORG    100H

MAIN               PROC

BEGIN:
                   JMP    START

HOUR               DB     ?                ;CURRENT HOUR IN BINARY
MINUTES            DB     ?                ;        MINUTES
SECONDS            DB     ?                ;        SECONDS

MONTH              DB     ?                ;CURRENT MONTH IN BINARY
DAY                DB     ?                ;        DAY
YEAR               DW     ?                ;        YEAR AS 2 DIGITS

START:             SUB    AX,AX            ;READ SECONDS FROM
                   CALL   READ_RTC         ; REAL-TIME CLOCK AND
                   MOV    SECONDS,AL       ; CONVERTED RESULT

                   MOV    AL,02H
                   CALL   READ_RTC         ;DO SAME FOR MINUTES
                   MOV    MINUTES,AL

                   MOV    AL,04H
                   CALL   READ_RTC         ;AND THE HOUR
                   MOV    HOUR,AL

                   MOV    AL,07H
                   CALL   READ_RTC         ;NOW THE DAY
                   MOV    DAY,AL

                   MOV    AL,08H
                   CALL   READ_RTC         ;MONTH
                   MOV    MONTH,AL

                   MOV    AL,09H
                   CALL   READ_RTC         ;AND YEAR
                   MOV    YEAR,AX

                   SUB    AX,AX            ;NOW, WE'LL CALL ON
                   MOV    AH,2DH
                   MOV    CH,HOUR          ; DOS TO SET THE
                   MOV    CL,MINUTES
                   MOV    DH,SECONDS       ; CORRECT TIME
                   MOV    DL,50

                   INT    21H              ;SET THE TIME

                   SUB    AX,AX            ;NEXT, WE'LL
                   MOV    AH,2BH           ; DO THE SAME
                   MOV    CX,1900          ; FOR THE DATE
                   ADD    CX,YEAR          ; IN CASE WE'VE
                   MOV    DH,MONTH         ; PASSED MIDNIGHT
                   MOV    DL,DAY           ; WITH INCORRECT TIME

                   INT    21H              ;SET THE DATE

                   MOV    AX,4C00H         ;RETURN TO DOS
                   INT    21H              ; WITH A ZERO ERRORLEVEL
                   ENDP

READ_RTC           PROC

                   OUT    70H,AL           ;LET PORT 70 KNOW WHAT
                   JMP    $+2              ; ADDRESS WE WANT AND
                   JMP    $+2              ; GIVE IT TIME TO RESPOND

                   IN     AL,71H           ;GET DATA FROM THE
                   JMP    $+2              ; REAL TIME CLOCK AND
                   JMP    $+2              ; GIVE IT TIME TO RESPOND

                   MOV    AH,AL            ;COPY BCD INFO TO AH
                   SHR    AH,1             ; AND SHIFT OUT
                   SHR    AH,1             ; THE LEAST SIGNIFICANT
                   SHR    AH,1             ; DIGIT, THEN CONVERT
                   SHR    AH,1             ; BOTH UNPACKED BCD
                   AND    AX,0F0FH         ; DIGITS TO BINARY
                   AAD                     ; IN AL

                   RET
                   ENDP

CSEG               ENDS
                   END    BEGIN


