home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload
/
ShartewareOverload.cdr
/
games
/
resetclk.zip
/
RESETCLK.TXT
< prev
Wrap
Text File
|
1991-08-14
|
10KB
|
241 lines
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