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