264 lines
8.7 KiB
Plaintext
264 lines
8.7 KiB
Plaintext
Some remarks on the usage of the Real Time Clock (RTC) MC 146818 of the AT.
|
|
Not as comprehensive as The_Serial_Port, but I'm working on it :-).
|
|
Chris
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
You should use the BIOS interrupt 1Ah to program the RTC. If you don't
|
|
like this or if you want to use some special features, here is how to
|
|
access the registers directly:
|
|
|
|
|
|
Reading an RTC register
|
|
-----------------------
|
|
cli ; make sure no interrupt wants to access the RTC in the mean time
|
|
mov al,address
|
|
out 70h,al
|
|
in al,71h
|
|
sti
|
|
|
|
_disable();
|
|
outp(0x70,address);
|
|
x=inp(0x71);
|
|
_enable();
|
|
|
|
|
|
Writing an RTC register
|
|
-----------------------
|
|
cli
|
|
mov al,address
|
|
out 70h,al
|
|
mov al,value
|
|
out 71h,al
|
|
sti
|
|
|
|
_disable();
|
|
outp(0x70,address);
|
|
outp(0x71,value);
|
|
_enable();
|
|
|
|
|
|
Make sure you rewrite the address before every access, even if it's identical
|
|
with the previous access! CPU interrupts should be disabled during your
|
|
access, but of course you don't need to do that explicitly every time if they
|
|
are disabled anyway (eg. in an interrupt handler).
|
|
|
|
The RTC generates interrupt 70h. You must enable level 2 of the master ICU and
|
|
level 0 of the slave ICU. (Clear bit 2 of port 21h and bit 0 of port 0A1h;
|
|
both registers are read/write). Both master and slave ICU expect an EOI (write
|
|
20h to 20h and 0A0h).
|
|
|
|
|
|
And these are the registers:
|
|
|
|
Address Function
|
|
--------------------------------------
|
|
0 actual second
|
|
1 alarm second
|
|
2 actual minute
|
|
3 alarm minute
|
|
4 actual hour
|
|
5 alarm hour
|
|
6 day of week (not used by the BIOS nor DOS)
|
|
7 day of month
|
|
8 month
|
|
9 year
|
|
A status A
|
|
B status B
|
|
C status C
|
|
D status D
|
|
E - 3F buffered memory that holds the BIOS setup
|
|
40 - 7F buffered memory available with some clones
|
|
|
|
|
|
status A (Control 1)
|
|
--------
|
|
Bit 0-3: interrupt frequency (PC: 0110b = 1024 ints per second)
|
|
4-6: xtal frequency (PC: 010b = 32,768 kHz)
|
|
7: UIP (update in progess: if 1, time is invalid)
|
|
UIP tells you when you can't read the registers 0, 2, 4, 6 - 9 at the
|
|
moment. It is usually high for less than a millisecond.
|
|
You can change bits 0-6. These are the values that produce useful results
|
|
in a PC:
|
|
|
|
Bits 6-4
|
|
--------
|
|
010 use 32,768 Hz xtal
|
|
others don't do anything useful in PCs, really...
|
|
|
|
Bits 3-0
|
|
--------
|
|
0000 seems to inhibit the whole RTC
|
|
0001 divides xtal by 128 (PC: 256 ints per second)
|
|
0010 divides xtal by 256 (PC: 128 ints per second)
|
|
0011 divides xtal by 4 (PC: 8192 ints per second)
|
|
0100 divides xtal by 8 (PC: 4096 ints per second)
|
|
0101 divides xtal by 16 (PC: 2048 ints per second)
|
|
0110 divides xtal by 32 (PC: 1024 ints per second)
|
|
0111 divides xtal by 64 (PC: 512 ints per second)
|
|
1000 divides xtal by 128 (PC: 256 ints per second)
|
|
1001 divides xtal by 256 (PC: 128 ints per second)
|
|
1010 divides xtal by 512 (PC: 64 ints per second)
|
|
1011 divides xtal by 1024 (PC: 32 ints per second)
|
|
1100 divides xtal by 2048 (PC: 16 ints per second)
|
|
1101 divides xtal by 4096 (PC: 8 ints per second)
|
|
1110 divides xtal by 8192 (PC: 4 ints per second)
|
|
1111 divides xtal by 16384 (PC: 2 ints per second)
|
|
|
|
Obviously you should only use values 0011 - 1111. Note also that
|
|
8192 ints per second is an awful lot for slow computers.
|
|
|
|
|
|
status B (Control 2)
|
|
--------
|
|
Bit 0: 1=daylight savings flag (German "Sommerzeit"-"summer time")
|
|
1: 0=12hr, 1=24hr mode (PC: 1)
|
|
2: 0=BCD, 1=binary mode (PC: 0)
|
|
3: 1=square wave generator on (PC: 0)
|
|
4: 1=generate time update interrupts (every second) (PC: 0)
|
|
5: 1=alarm interrupt (int when alarm time reached) (PC: 0)
|
|
6: 1=generate periodic interrupt (see status A) (PC: 0)
|
|
7: 1=inhibit time increment (while setting the clock)
|
|
|
|
status C (interrupt cause)
|
|
--------
|
|
Bit 4: 1=time changed (generated every second)
|
|
5: 1=alarm time reached
|
|
6: 1=periodic int
|
|
others not defined
|
|
|
|
status D (battery)
|
|
--------
|
|
Bit 7: 1=battery OK, 0=battery weak
|
|
others undefined
|
|
|
|
|
|
When servicing interrupts, you MUST read status C! That's the chip's inter-
|
|
rupt acknowledge. Make sure to read this register after programming the
|
|
interrupt control register (status B), or you won't get any interrupts.
|
|
|
|
The Day Of Week - Counter counts from 1 to 7 and restarts with 1 again. When
|
|
0, it is not incremented. On a PC, it's always 0 if you don't set it to a
|
|
specific value. (Ralf Brown says 1=sunday, but not with my PCs.)
|
|
|
|
|
|
Using the BIOS instead
|
|
======================
|
|
|
|
[sneaked from Ralf Brown's interrupt list version 31]
|
|
|
|
|
|
----------1A02-------------------------------
|
|
INT 1A - TIME - GET REAL-TIME CLOCK TIME (AT,XT286,PS)
|
|
AH = 02h
|
|
Return: CF clear if successful
|
|
CH = hour (BCD)
|
|
CL = minutes (BCD)
|
|
DH = seconds (BCD)
|
|
DL = daylight savings flag (00h standard time, 01h daylight time)
|
|
CF set on error (i.e. clock not running or in middle of update)
|
|
SeeAlso: AH=00h
|
|
----------1A03-------------------------------
|
|
INT 1A - TIME - SET REAL-TIME CLOCK TIME (AT,XT286,PS)
|
|
AH = 03h
|
|
CH = hour (BCD)
|
|
CL = minutes (BCD)
|
|
DH = seconds (BCD)
|
|
DL = daylight savings flag (00h standard time, 01h daylight time)
|
|
SeeAlso: AH=01h
|
|
----------1A04-------------------------------
|
|
INT 1A - TIME - GET REAL-TIME CLOCK DATE (AT,XT286,PS)
|
|
AH = 04h
|
|
Return: CF clear if successful
|
|
CH = century (BCD)
|
|
CL = year (BCD)
|
|
DH = month (BCD)
|
|
DL = day (BCD)
|
|
CF set on error
|
|
SeeAlso: AH=02h,AH=05h,INT 21/AH=2Ah
|
|
----------1A05-------------------------------
|
|
INT 1A - TIME - SET REAL-TIME CLOCK DATE (AT,XT286,PS)
|
|
AH = 05h
|
|
CH = century (BCD)
|
|
CL = year (BCD)
|
|
DH = month (BCD)
|
|
DL = day (BCD)
|
|
SeeAlso: AH=04h,INT 21/AH=2Bh
|
|
----------1A06-------------------------------
|
|
INT 1A - TIME - SET ALARM (AT,XT286,PS)
|
|
AH = 06h
|
|
CH = hour (BCD)
|
|
CL = minutes (BCD)
|
|
DH = seconds (BCD)
|
|
Return: CF set on error (alarm already set or clock stopped for update)
|
|
CF clear if successful
|
|
Note: the alarm occurs every 24 hours until turned off, invoking INT 4A each
|
|
time
|
|
SeeAlso: AH=07h,INT 4A
|
|
----------1A07-------------------------------
|
|
INT 1A - TIME - CANCEL ALARM (AT,XT286,PS)
|
|
AH = 07h
|
|
Return: alarm disabled
|
|
Note: does not disable the real-time clock's IRQ
|
|
SeeAlso: AH=06h,INT 70
|
|
|
|
If you wish to reset the system clock according to the RTC, call int 21h
|
|
function 2Dh. Note that starting with DOS 3.3, this also influences the
|
|
RTC!! See below.
|
|
|
|
----------212C-------------------------------
|
|
INT 21 - DOS 1+ - GET SYSTEM TIME
|
|
AH = 2Ch
|
|
Return: CH = hour
|
|
CL = minute
|
|
DH = second
|
|
DL = 1/100 seconds
|
|
Note: on most systems, the resolution of the system clock is about 5/100sec,
|
|
so returned times generally do not increment by 1
|
|
on some systems, DL may always return 00h
|
|
SeeAlso: AH=2Ah,AH=2Dh,AH=E7h,INT 1A/AH=00h,INT 1A/AH=02h,INT 1A/AH=FEh
|
|
SeeAlso: INT 2F/AX=120Dh
|
|
----------212D-------------------------------
|
|
INT 21 - DOS 1+ - SET SYSTEM TIME
|
|
AH = 2Dh
|
|
CH = hour
|
|
CL = minute
|
|
DH = second
|
|
DL = 1/100 seconds
|
|
Return: AL = result
|
|
00h successful
|
|
FFh invalid time, system time unchanged
|
|
Note: DOS 3.3+ also sets CMOS clock
|
|
SeeAlso: AH=2Bh"DOS",AH=2Ch,INT 1A/AH=01h,INT 1A/AH=03h,INT 1A/AH=FFh"AT&T"
|
|
|
|
A better method to reset the system clock is to calculate the number of
|
|
timer clicks since midnight and write it to the BIOS variable 0h:46Ch
|
|
(or call int 1Ah). This does not affect the precision of the RTC like calls
|
|
to 212D do.
|
|
|
|
0h:46Ch DWORD Timer ticks since midnight
|
|
0h:470h BYTE Timer overflow, non-zero if has counted past midnight
|
|
|
|
----------1A00-------------------------------
|
|
INT 1A - TIME - GET SYSTEM TIME
|
|
AH = 00h
|
|
Return: CX:DX = number of clock ticks since midnight
|
|
AL = midnight flag, nonzero if midnight passed since time last read
|
|
Notes: there are approximately 18.2 clock ticks per second, 1800B0h per 24 hrs
|
|
IBM and many clone BIOSes set the flag for AL rather than incrementing
|
|
it, leading to loss of a day if two consecutive midnights pass
|
|
without a request for the time (e.g. if the system is on but idle)
|
|
SeeAlso: AH=01h,AH=02h,INT 21/AH=2Ch
|
|
----------1A01-------------------------------
|
|
INT 1A - TIME - SET SYSTEM TIME
|
|
AH = 01h
|
|
CX:DX = number of clock ticks since midnight
|
|
SeeAlso: AH=00h,AH=03h,INT 21/AH=2Dh
|
|
|
|
BTW: the exact value is 18.2064819335 clicks per second (or 1193180/65536).
|
|
You can calculate these values without floating point arithmetics!
|
|
|
|
|
|
Ralf Brown's interrupt list is available from several ftp sites. Ask an archie
|
|
server for "prog inter3". I think the actual version is 38.
|