In order to keep code size at a minimum, OS$CLOCK only supports clock services for an XT, although it does provide extra features to make the XT clock services behave more like those of an AT. The AT system provides a real-time clock, while the XT system normally does not.
The XT system has two basic problems in dealing with the date and time:
The "warm-boot" problem is solved by re-vectoring the keyboard interrupt handler (Int 15, Function 4FH) to the OS$CLOCK device driver where a check is made for the warm-boot Ctrl-Alt-Del key sequence. If this key sequence is detected, the current time and date is stored in an unused BIOS data area. When the OS$CLOCK initialize function is called, it reads back the time and date information, and properly updates the time and date. More detail on the keyboard interrupt handler is given later.
The "date loss" problem is solved by re-vectoring the timer tick interrupt handler (Int 1CH) to the OS$CLOCK device driver, where it checks for a midnight rollover, and adjusts the date count appropriately. The date information is stored in a word as the number of days elapsed since January 1, 1980. This information is normally kept by the standard device driver -- OS$CLOCK keeps its own version of this value. More detail on the timer tick interrupt handler is given later.
Listed below are some of the other salient features of OS$CLOCK:
The OS$CLOCK driver is made up of three parts:
Table 3-1 Request Header Leading Bytes
BYTE FIELD MEANING OFFSET LENGTH -------------------------------------------------------------------- 00H Byte Length of the request header. 01H Byte Unit Code: the device number for block devices (OS$CLOCK is a character device). 02H Byte Command Code: the number of the most recent command sent to the driver. 03H Word Status: status code set by the driver after each call. If bit 15 is set, an error code exists in the lower byte. A status code of 0 indicates a successful completion. The status bytes are listed below: Bits FEDCBA98 76543210 Meaning -------- -------- --------------------------------- 1....... 00000001 Write-protect violation error 1....... 00000010 Unknown unit error 1....... 00000011 Drive not ready error 1....... 00000100 CRC error 1....... 00000101 Bad drive request structure len. 1....... 00000110 Seek error 1....... 00000111 Unknown media error 1....... 00001000 Sector not found error 1....... 00001001 Printer out of paper error 1....... 00001010 Write fault 1....... 00001011 Read fault 1....... 00001100 General failure .......x ........ Done ......x. ........ Busy .xxxxx.. ........ Reserved 0....... ........ No error 05H 8 Bytes Reserved for use by DOS 0DH Variable Data required by the driverThe error bit (bit 15) in the status word is set to indicate that an error occurred in the operation of the driver. The error code is returned in the lower 8 bits of the status word.
The busy bit is set if the device driver was busy when called. The done bit is set when the OS$CLOCK has completed its operation.
The interrupt routine is poorly named; a device driver is actually invoked with a call from MS-DOS, and returns with a RET (return from call), not an IRET (return from interrupt). So it is not really an interrupt routine. Original plans to have a device driver interrupt MS-DOS when it could take care of its next task have not yet materialized.
Once called, it begins by saving the present machine state on the stack, which is typical of called routines. Then the request header is retrieved from the location at which the strategy routine stored it. The OS$CLOCK device driver then determines the function it is supposed to perform by examining the byte at offset 02H into the request header. A check is made to ensure the command is legal; if it is, the interrupt routine branches to the code location that handles that function. If the command is illegal (larger than the maximum command number) the driver returns with an error flag to indicate that the command was unknown.
OS$CLOCK supports the functions defined by the command codes listed in Table 3-2.
Table 3-2 Command Codes Supported
Code Function ------------------------------------ 00H Initialize 01H Media Check (1) 02H Build BPB (1) 04H Read 05H Nondestructive Read 06H Input Status (1) 07H Flush Input Buffers (1) 08H Write 09H Write with Verify 0AH Output Status (1) 0BH Flush Output Buffers (1) Note: 1. These command codes do nothing but set the 'done' bit in the status word and returnAny other command code given to OS$CLOCK results in the 'done' and 'error' bits being set in the upper byte of the status word, and an error code of 03H being returned in the lower byte, indicating that the command was unknown.
The request header contains the following bytes on entry to the initialize function:
Request Header Length Description Offset ------------------------------------------------------------------ 02H byte command code (OOH) 12H dword segment, offset of CONFIG.SYS command line 16H byte next available MS-DOS drive numberThe request header contains the following bytes on returning from the initialize function:
Request Header Length Description Offset ------------------------------------------------------------------ 03H word status ODH byte number of units OEH dword segment, offset of end of driver 12H dword segment, offset of BIOS parameter block (BPB)The initialize function code checks the CONGIG.SYS command line dealing with OS$CLOCK If it finds a /D switch, the version number and copyright message is displayed, as follows:
OS$CLOCK Clock device driver V1.00 Copyright (c) 1990 Poqet Computer Corp. All rights reservedAs part of the initialization, the OS$CLOCK device driver code also checks the interrupt communication area (ICA) located at 40:00F0H to find out if it contains valid date and time information. The validity of the data is verified by using a checksum byte. The format of the stored data should be as follows:
Segment:Offset Length Description ------------------------------------ 40:00F0H word date 40:00F2H dword timer ticks 40:00F6H byte checksumIf the checksum is valid for the data given, the date is stored in a word within the OS$CLOCK driver. The timer ticks are read from the BIOS Int 1AH, service 00H. Timer ticks are returned in CX and DX. The timer ticks count located at 40:00F2H is added to the value in CX and DX, plus an adjustment to allow for the time the timer interrupt was disabled during power-on reset. The result is used to set a new time using BIOS Int 1AH, service 01H. When the two times are added together, a check is made for rollover. If rollover occurs, the date will be incremented.
Rollover occurs when the tick count is greater than or equal to 001800B0H. This value is equal to the total number of clock ticks in a 24-hour period. After a new time is set, the checksum at 40:00F6H is inverted to indicate that the data is no longer valid.
Before a return to MS-DOS is executed, a call is made to the extended BIOS Int 66H, function 02H, service 06H, to disable the UART power supply. This saves system power when the serial port is not being used.
After the UART is powered down, the 'done' bit in the status word is set, the current code segment and offset are loaded into the request header at offset 02H, and the initialize function returns to MS-DOS. The segment and offset values point to the start of the initialization routine. This is because it is executed only once at boot-up. The initialization routine is placed at the end driver so that the memory it occupies can be released after the code finishes executing.
The number of units and the BPB segment and offset are not used by MS-DOS for OS$CLOCK.
The request header contains the following bytes on entry to the read function:
Request Header Length Description Offset ------------------------------------------------------- 02H byte command code (04H) 0EH dword transfer address 12H word number of bytes to transferThe request header contains the following bytes on returning from the read function:
Request Header Length Description Offset ------------------------------------------------------- 03H word status 12H word number of bytes actually transferredThe six bytes that are transferred are in the following format:
Byte Number Description ------------------------------------------ +00 date (low byte) +01 date (high byte) +02 minutes +03 hours +04 seconds +05 hundredths of seconds
Request Header Length Description Offset ------------------------------------------------------- 02H byte command code (05H)The request header contains the following bytes on returning from the nondestructive read function:
Request Header Length Description Offset ------------------------------------------------------- 03H word statusThe result is simply that the 'busy' and 'done' bits are set in the status word.
The number of bytes to be read, then written, is contained in offset 12H of the request header. The function returns to MS-DOS after setting the 'done' bit in the status word.
The request header contains the following bytes on entry to the write function:
Request Header Length Description Offset ------------------------------------------------------- 02H byte command code (08H) 0EH dword transfer address 12H word number of bytes to transferThe request header contains the following bytes on returning from the write function:
Request Header Length Description Offset ------------------------------------------------------- 03H word status 12H word number of bytes actually transferred
Control is then returned to MS-DOS through an IRET instruction, as though program execution is returning from the timer tick interrupt.
The status of the Ctrl and Alt keys are checked by examining byte 40:0017. If the Ctrl key is depressed, bit 2 will be set. If the Alt key is depressed, bit 3 will be set. If both keys are depressed, OS$CLOCK reads the scan code from I/O port 60H. If the scan code is 53H, indicating the Del key is depressed, a warm boot is about to occur.
If this is the case, OS$CLOCK restores the original Int 09H interrupt vector and reads the time by using BIOS Int 1A, service 0. This value is then stored in the ICA together with the current value of the date count. A checksum is computed on the first 6 bytes in the ICA, and the result is stored in the next byte. The locations and format of the data in the ICA is as described previously in the initialize function.
A far jump is then made to the original keyboard interrupt vector -- this occurs whether a CTRL-ALT-DEL sequence is detected or not.