home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Boston 2
/
boston-2.iso
/
DOS
/
HILFEN
/
SYSTEM
/
ATCLOK
/
ATCLOCK.ASM
next >
Wrap
Assembly Source File
|
1993-12-01
|
11KB
|
553 lines
page 60, 132
;
; atclock.asm
;
; An MS-DOS clock device driver that uses the AT real-time
; clock rather than the BIOS time-of-day.
;
; Placed in the public domain.
; D. Rifkind 13 Jan 90
;
;
; Request header structure
;
; This is the structure of a device driver request header
; for read and write requests, the only ones (other than
; initialization) supported.
;
reqhdr_t struc
rh_length db ? ; Length of request header
rh_unit db ? ; Unit number (ignored)
rh_command db ? ; Command code
rh_status dw ? ; Return status location
db 8 dup (?) ; Reserved
rh_media db ? ; Media descriptor (ignored)
rh_address dd ? ; Transfer address (far pointer)
rh_count dw ? ; Transfer length
rh_sector dw ? ; Starting sector number (ignored)
reqhdr_t ends
;
; This is the structure of the request header for the
; initialization call. The first 13 bytes are the same as
; for all other requests, and not duplicated here.
;
reqinit_t struc
db 13 dup (?) ; Duplicates other requests
ri_nunits db ? ; Number of units (not supported)
ri_endaddr dd ? ; Address of end of driver
ri_bpbptr dd ? ; BPB pointer (not supported)
reqinit_t ends
;
; Start of driver
;
clock segment
assume cs:clock
;
; Device driver header
;
dd_link dw -1, -1 ; Link to next driver: none
dd_attrib dw 8008h ; Device attribute word:
; 8000h - is a character device
; 0008h - is the clock device
dd_stratptr dw strategy ; Offset of strategy entry point
dd_intptr dw interrupt ; Offset of interrupt entry point
dd_name db "CLOCK$ " ; Device name (8 characters)
;
; Driver data area
;
reqptr label dword ; Pointer to I/O request header
reqptr_off dw ?
reqptr_seg dw ?
;
; Commands table
;
; Array of pointers to individual command handlers. A
; zero entry is an unimplemented command.
;
commands label word
dw clk_initialize ; 0 - initialize
dw 0 ; 1 - media check
dw 0 ; 2 - build BPB
dw 0 ; 3 - IOCTL input
dw clk_input ; 4 - input
dw clk_input ; 5 - nondestructive input
dw clk_noop ; 6 - input status
dw clk_noop ; 7 - input flush
dw clk_output ; 8 - output
dw clk_output ; 9 - output with verify
dw clk_noop ; 10 - output status
dw clk_noop ; 11 - output flush
NCOMMANDS equ ($-commands)/2
;
; Strategy entry point
;
; Like most DOS device drivers, this strategy entry does
; nothing but save a pointer to the request header for use
; by the interrupt routine.
;
strategy proc far
assume ds:nothing
mov cs:reqptr_off, bx
mov cs:reqptr_seg, es
ret
strategy endp
;
; Interrupt entry point
;
; Sorely misnamed, this routine is called by DOS after
; passing the address of the request header to the
; strategy routine. This driver handles only a few
; requests: read (and nondestructive read), write (and
; write with verify), and initialize.
;
interrupt proc far
assume ds:nothing
push ax ; Save the world
push bx
push cx
push dx
push bp
push si
push di
push ds
push es
mov ax, cs ; Point DS to driver segment
mov ds, ax
assume ds:clock
les di, reqptr ; ES:DI points to request header
mov es:rh_status[di], 8003h
; Assume invalid command
mov bl, es:rh_command[di]
cmp bl, NCOMMANDS ; Check command in range
jae interrupt_return
xor bh, bh
shl bx, 1
cmp commands[bx], 0 ; Check for unimplemented command
jz interrupt_return
call commands[bx] ; Execute command
les di, reqptr ; Assume the command stepped on this
mov es:rh_status[di], ax
; Set return status
interrupt_return:
pop es ; Restore old state
pop ds
assume ds:nothing
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
ret
interrupt endp
assume ds:clock ; For remainder of driver
;
; month_date
;
; Used for date conversions. A table of the number of
; days from the start of the year to the start of a given
; month, for non-leap years.
;
month_date label word
dw 0
dw 31
dw 59
dw 90
dw 120
dw 151
dw 181
dw 212
dw 243
dw 273
dw 304
dw 334
dw 365
;
; clk_noop
;
; Called for do-nothing requests that return "done" status
; and nothing else.
;
clk_noop proc near
mov ax, 100h ; "Done"
ret
clk_noop endp
;
; clk_input
;
; Reads the time-of-day clock using INT 1Ah services.
;
clk_input proc near
cmp es:rh_count[di], 6
je clk_input_1 ; Only valid requests supported
mov ax, 800Bh ; Call it a read fault
ret
clk_input_1:
les di, es:rh_address[di]
; Point to transfer address
clk_input_again:
mov ah, 4h
int 1Ah ; Get current date...
push cx ; ...and save it
push dx
mov ah, 2h
int 1Ah ; Get current time
mov al, dh
call frombcd ; Convert to binary
mov es:[di+5], al ; Current seconds
mov byte ptr es:[di+4], 0
; Clock has only 1-sec. resolution
mov al, cl
call frombcd
mov es:[di+2], al ; Current minutes
mov al, ch
call frombcd
mov es:[di+3], al ; Current hours
mov ah, 4h
int 1Ah ; Get date again
pop bx ; Restore saved date
pop ax
cmp bx, dx ; Check whether date has changed,
jne clk_input_again ; and go try again if it has
cmp ax, cx
jne clk_input_again
; Now we have the INT 1Ah date in CX and DX and need to
; convert it to days since 1 Jan 80. Start with the
; number of days to the start of this month.
mov al, dh
call frombcd ; Month of year
dec ax
mov bx, ax
shl bx, 1
mov ax, month_date[bx]
; Number of days to start of month
mov es:[di+0], ax
; If the current year is a leap year and the current month
; is March or later, bump the date to make up for the leap
; day.
mov al, cl
call frombcd
test al, 3h ; Leap year?
jnz clk_input_2 ; No - skip it
cmp dh, 3 ; March or later?
jb clk_input_2 ; No - skip it
inc word ptr es:[di+0]
clk_input_2:
; Add in the current day of the month.
mov al, dl
call frombcd ; Day of month
dec ax
add word ptr es:[di+0], ax
; Now we need the current year, 1980-based. I hope we can
; do better than MS-DOS by 2000, but I'll check anyhow.
mov al, cl
call frombcd ; Year within century
cmp ch, 20h ; Is it 2000 AD yet?
jb clk_input_3
add ax, 100 ; Yer puttin' me on...
clk_input_3:
sub ax, 80 ; Base on 1980
mov cx, ax ; Save for later
mov bx, 365
mul bx
add word ptr es:[di+0], ax
; Now fix up for leap years before the current year.
add cx, 3
shr cx, 1
shr cx, 1 ; Leap years before this year
add word ptr es:[di+0], cx
; That's it!
mov ax, 100h
ret
clk_input endp
;
; clk_output
;
; Sets the real-time clock date and time.
;
; For those who want to do date conversions in the worst
; possible way, this is it: the worst possible way. I
; assume that setting the clock is a rare operation, so I
; don't care if it takes ten times as long as necessary,
; and just want to keep the code size down.
;
clk_output proc near
cmp es:rh_count[di], 6
je clk_output_1 ; Only valid requests supported
mov ax, 800Ah ; Call it a write fault
ret
clk_output_1:
les di, es:rh_address[di]
; The hard part is converting the number-of-days-since-1-
; Jan-80 field to year, month, and day. We start by
; counting years.
mov ax, es:[di+0] ; Number of days...
xor cx, cx ; CX = year past 1980
clk_output_2:
mov bl, cl
and bl, 3h
cmp bl, 1 ; Carry set if leap year
mov bx, 0 ; Don't use XOR 'cause we need CF
adc bx, 365 ; Number of days this year
cmp ax, bx
jb clk_output_3
sub ax, bx
inc cx
jmp clk_output_2
clk_output_3:
; Now we need to know the month. Scan through the months
; table looking for the first one starting after this
; date. The months table has an extra entry to make sure
; this ends properly. Some incredibly abstruse fiddling
; with carries handles leap years.
mov bx, 2 ; No sense worrying about January
xor si, si ; Holds previous month's start
clk_output_4:
mov dl, cl
and dl, 3h
cmp bx, 4 ; Index for March in table
adc dl, 0
cmp dl, 1 ; Carry set if leap year and month
; March or later
mov dx, month_date[bx]
adc dx, 0 ; Fix up leap year dates
cmp ax, dx
jb clk_output_5
mov si, dx
inc bx
inc bx
jmp clk_output_4
clk_output_5:
sub ax, si ; Day of month (0-based)
mov dl, al
shr bx, 1 ; Month (1-based)
mov dh, bl
; Now put that all aside for the moment. We're going to
; set the time of day (to nothing useful) to make sure it
; doesn't flip the date over while we're updating things.
push cx
push dx
mov ah, 2h
int 1Ah ; Just to get the DST flag
xor cx, cx
xor dh, dh
mov ah, 3h
int 1Ah
; Date format needs a little adjusting. Convert year-1980
; to century/year, day to 1-based, and everything to BCD.
pop dx
mov al, dl
inc al
call tobcd ; Day of month
mov dl, al
mov al, dh
call tobcd ; Month
mov dh, al
pop ax ; Year - 1980
add ax, 80
mov ch, 19h
cmp ax, 100
jb clk_output_6
mov ch, 20h
sub ax, 100
clk_output_6:
call tobcd ; Year within century
mov cl, al
; Phew. Now we can set the date.
mov ah, 5h
int 1Ah
; Setting the time is a breeze. Just convert parts of it
; to BCD and go.
mov ah, 2h
int 1Ah ; To get DST flag
mov al, es:[di+5]
call tobcd ; Seconds
mov dh, al
mov al, es:[di+2]
call tobcd ; Minutes
mov cl, al
mov al, es:[di+3]
call tobcd ; Hours
mov ch, al
mov ah, 3h
int 1Ah ; Set time
; Done.
mov ax, 100h
ret
clk_output endp
;
; frombcd
;
; Converts a two-digit BCD number in AL to its binary
; equivalent in AX. Uses AX and BX but preserves all
; other registers.
;
frombcd proc near
mov bl, al
and al, 0Fh ; One's digit
and bl, 0F0h ; Ten's digit
shr bl, 1
add al, bl ; One's + (ten's * 8)...
shr bl, 1
shr bl, 1
add al, bl ; ...+ (ten's * 2)
xor ah, ah
ret
frombcd endp
;
; tobcd
;
; Converts a binary number in AL to its BCD equivalent.
; This is a rotten way to do a conversion, but I don't
; care. Used only when setting the clock, so it's not
; time-critical. AL on entry must be less than 100.
;
tobcd proc near
xor ah, ah
tobcd_loop:
cmp al, 10
jb tobcd_return
sub al, 10
add ah, 10h
jmp tobcd_loop
tobcd_return:
or al, ah
ret
tobcd endp
;
; End of resident part of driver
;
clock_end equ $
;
; clk_initialize
;
; Driver initialization. Very little to do here except
; set the driver end address.
;
clk_initialize proc near
; Check whether we really have a CMOS clock.
mov ah, 2h
stc
int 1Ah
jnc clk_initialize_1
; No clock. Some silly person must be trying to install
; us on a PC or XT. We'll show them!
mov dd_attrib, 0
mov es:ri_nunits[di], 0
mov word ptr es:ri_endaddr+0[di], 0
mov word ptr es:ri_endaddr+2[di], cs
mov ax, 100h
ret
clk_initialize_1:
mov word ptr es:ri_endaddr+0[di], offset clock_end
mov word ptr es:ri_endaddr+2[di], cs
mov ax, 100h
ret
clk_initialize endp
clock ends
end