home *** CD-ROM | disk | FTP | other *** search
/ Boston 2 / boston-2.iso / DOS / HILFEN / SYSTEM / ATCLOK / ATCLOCK.ASM next >
Assembly Source File  |  1993-12-01  |  11KB  |  553 lines

  1.     page    60, 132
  2. ;
  3. ;    atclock.asm
  4. ;
  5. ;    An MS-DOS clock device driver that uses the AT real-time
  6. ;    clock rather than the BIOS time-of-day.
  7. ;
  8. ;    Placed in the public domain.
  9. ;    D. Rifkind  13 Jan 90
  10. ;
  11.  
  12. ;
  13. ;    Request header structure
  14. ;
  15. ;    This is the structure of a device driver request header
  16. ;    for read and write requests, the only ones (other than
  17. ;    initialization) supported.
  18. ;
  19.  
  20. reqhdr_t    struc
  21.   rh_length    db    ?        ; Length of request header
  22.   rh_unit    db    ?        ; Unit number (ignored)
  23.   rh_command    db    ?        ; Command code
  24.   rh_status    dw    ?        ; Return status location
  25.         db    8 dup (?)    ; Reserved
  26.   rh_media    db    ?        ; Media descriptor (ignored)
  27.   rh_address    dd    ?        ; Transfer address (far pointer)
  28.   rh_count    dw    ?        ; Transfer length
  29.   rh_sector    dw    ?        ; Starting sector number (ignored)
  30. reqhdr_t    ends
  31.  
  32. ;
  33. ;    This is the structure of the request header for the
  34. ;    initialization call.  The first 13 bytes are the same as
  35. ;    for all other requests, and not duplicated here.
  36. ;
  37.  
  38. reqinit_t    struc
  39.         db    13 dup (?)    ; Duplicates other requests
  40.   ri_nunits    db    ?        ; Number of units (not supported)
  41.   ri_endaddr    dd    ?        ; Address of end of driver
  42.   ri_bpbptr    dd    ?        ; BPB pointer (not supported)
  43. reqinit_t    ends
  44.  
  45. ;
  46. ;    Start of driver
  47. ;
  48.  
  49. clock        segment
  50.         assume    cs:clock
  51.  
  52. ;
  53. ;    Device driver header
  54. ;
  55.  
  56. dd_link        dw    -1, -1        ; Link to next driver: none
  57. dd_attrib    dw    8008h        ; Device attribute word:
  58.                     ;   8000h - is a character device
  59.                     ;   0008h - is the clock device
  60. dd_stratptr    dw    strategy    ; Offset of strategy entry point
  61. dd_intptr    dw    interrupt    ; Offset of interrupt entry point
  62. dd_name        db    "CLOCK$  "    ; Device name (8 characters)
  63.  
  64. ;
  65. ;    Driver data area
  66. ;
  67.  
  68. reqptr        label    dword        ; Pointer to I/O request header
  69. reqptr_off    dw    ?
  70. reqptr_seg    dw    ?
  71.  
  72. ;
  73. ;    Commands table
  74. ;
  75. ;    Array of pointers to individual command handlers.  A
  76. ;    zero entry is an unimplemented command.
  77. ;
  78.  
  79. commands    label    word
  80.         dw    clk_initialize    ;  0 - initialize
  81.         dw    0        ;  1 - media check
  82.         dw    0        ;  2 - build BPB
  83.         dw    0        ;  3 - IOCTL input
  84.         dw    clk_input    ;  4 - input
  85.         dw    clk_input    ;  5 - nondestructive input
  86.         dw    clk_noop    ;  6 - input status
  87.         dw    clk_noop    ;  7 - input flush
  88.         dw    clk_output    ;  8 - output
  89.         dw    clk_output    ;  9 - output with verify
  90.         dw    clk_noop    ; 10 - output status
  91.         dw    clk_noop    ; 11 - output flush
  92. NCOMMANDS    equ    ($-commands)/2
  93.  
  94. ;
  95. ;    Strategy entry point
  96. ;
  97. ;    Like most DOS device drivers, this strategy entry does
  98. ;    nothing but save a pointer to the request header for use
  99. ;    by the interrupt routine.
  100. ;
  101.  
  102. strategy    proc    far
  103.         assume    ds:nothing
  104.  
  105.         mov    cs:reqptr_off, bx
  106.         mov    cs:reqptr_seg, es
  107.         ret
  108.  
  109. strategy    endp
  110.  
  111. ;
  112. ;    Interrupt entry point
  113. ;
  114. ;    Sorely misnamed, this routine is called by DOS after
  115. ;    passing the address of the request header to the
  116. ;    strategy routine.  This driver handles only a few
  117. ;    requests: read (and nondestructive read), write (and
  118. ;    write with verify), and initialize.
  119. ;
  120.  
  121. interrupt    proc    far
  122.         assume    ds:nothing
  123.  
  124.         push    ax        ; Save the world
  125.         push    bx
  126.         push    cx
  127.         push    dx
  128.         push    bp
  129.         push    si
  130.         push    di
  131.         push    ds
  132.         push    es
  133.  
  134.         mov    ax, cs        ; Point DS to driver segment
  135.         mov    ds, ax
  136.         assume    ds:clock
  137.  
  138.         les    di, reqptr    ; ES:DI points to request header
  139.  
  140.         mov    es:rh_status[di], 8003h
  141.                     ; Assume invalid command
  142.         mov    bl, es:rh_command[di]
  143.         cmp    bl, NCOMMANDS    ; Check command in range
  144.         jae    interrupt_return
  145.         xor    bh, bh
  146.         shl    bx, 1
  147.         cmp    commands[bx], 0    ; Check for unimplemented command
  148.         jz    interrupt_return
  149.  
  150.         call    commands[bx]    ; Execute command
  151.         les    di, reqptr    ; Assume the command stepped on this
  152.         mov    es:rh_status[di], ax
  153.                     ; Set return status
  154.  
  155. interrupt_return:
  156.         pop    es        ; Restore old state
  157.         pop    ds
  158.         assume    ds:nothing
  159.         pop    di
  160.         pop    si
  161.         pop    bp
  162.         pop    dx
  163.         pop    cx
  164.         pop    bx
  165.         pop    ax
  166.         ret
  167.  
  168. interrupt    endp
  169.  
  170.         assume    ds:clock    ; For remainder of driver
  171.  
  172. ;
  173. ;    month_date
  174. ;
  175. ;    Used for date conversions.  A table of the number of
  176. ;    days from the start of the year to the start of a given
  177. ;    month, for non-leap years.
  178. ;
  179.  
  180. month_date    label    word
  181.         dw    0
  182.         dw    31
  183.         dw    59
  184.         dw    90
  185.         dw    120
  186.         dw    151
  187.         dw    181
  188.         dw    212
  189.         dw    243
  190.         dw    273
  191.         dw    304
  192.         dw    334
  193.         dw    365
  194.  
  195. ;
  196. ;    clk_noop
  197. ;
  198. ;    Called for do-nothing requests that return "done" status
  199. ;    and nothing else.
  200. ;
  201.  
  202. clk_noop    proc    near
  203.  
  204.         mov    ax, 100h    ; "Done"
  205.         ret
  206.  
  207. clk_noop    endp
  208.  
  209. ;
  210. ;    clk_input
  211. ;
  212. ;    Reads the time-of-day clock using INT 1Ah services.
  213. ;
  214.  
  215. clk_input    proc    near
  216.  
  217.         cmp    es:rh_count[di], 6
  218.         je    clk_input_1    ; Only valid requests supported
  219.  
  220.         mov    ax, 800Bh    ; Call it a read fault
  221.         ret
  222.  
  223. clk_input_1:
  224.         les    di, es:rh_address[di]
  225.                     ; Point to transfer address
  226.  
  227. clk_input_again:
  228.         mov    ah, 4h
  229.         int    1Ah        ; Get current date...
  230.         push    cx        ; ...and save it
  231.         push    dx
  232.  
  233.         mov    ah, 2h
  234.         int    1Ah        ; Get current time
  235.  
  236.         mov    al, dh
  237.         call    frombcd        ; Convert to binary
  238.         mov    es:[di+5], al    ; Current seconds
  239.  
  240.         mov    byte ptr es:[di+4], 0
  241.                     ; Clock has only 1-sec. resolution
  242.  
  243.         mov    al, cl
  244.         call    frombcd
  245.         mov    es:[di+2], al    ; Current minutes
  246.  
  247.         mov    al, ch
  248.         call    frombcd
  249.         mov    es:[di+3], al    ; Current hours
  250.  
  251.         mov    ah, 4h
  252.         int    1Ah        ; Get date again
  253.         pop    bx        ; Restore saved date
  254.         pop    ax
  255.         cmp    bx, dx        ; Check whether date has changed,
  256.         jne    clk_input_again    ;   and go try again if it has
  257.         cmp    ax, cx
  258.         jne    clk_input_again
  259.  
  260. ;    Now we have the INT 1Ah date in CX and DX and need to
  261. ;    convert it to days since 1 Jan 80.  Start with the
  262. ;    number of days to the start of this month.
  263.  
  264.         mov    al, dh
  265.         call    frombcd        ; Month of year
  266.         dec    ax
  267.         mov    bx, ax
  268.         shl    bx, 1
  269.         mov    ax, month_date[bx]
  270.                     ; Number of days to start of month
  271.         mov    es:[di+0], ax
  272.  
  273. ;    If the current year is a leap year and the current month
  274. ;    is March or later, bump the date to make up for the leap
  275. ;    day.
  276.  
  277.         mov    al, cl
  278.         call    frombcd
  279.         test    al, 3h        ; Leap year?
  280.         jnz    clk_input_2    ; No - skip it
  281.         cmp    dh, 3        ; March or later?
  282.         jb    clk_input_2    ; No - skip it
  283.         inc    word ptr es:[di+0]
  284. clk_input_2:
  285.  
  286. ;    Add in the current day of the month.
  287.  
  288.         mov    al, dl
  289.         call    frombcd        ; Day of month
  290.         dec    ax
  291.         add    word ptr es:[di+0], ax
  292.  
  293. ;    Now we need the current year, 1980-based.  I hope we can
  294. ;    do better than MS-DOS by 2000, but I'll check anyhow.
  295.  
  296.         mov    al, cl
  297.         call    frombcd        ; Year within century
  298.         cmp    ch, 20h        ; Is it 2000 AD yet?
  299.         jb    clk_input_3
  300.         add    ax, 100        ; Yer puttin' me on...
  301. clk_input_3:
  302.         sub    ax, 80        ; Base on 1980
  303.         mov    cx, ax        ; Save for later
  304.         mov    bx, 365
  305.         mul    bx
  306.         add    word ptr es:[di+0], ax
  307.  
  308. ;    Now fix up for leap years before the current year.
  309.  
  310.         add    cx, 3
  311.         shr    cx, 1
  312.         shr    cx, 1        ; Leap years before this year
  313.         add    word ptr es:[di+0], cx
  314.  
  315. ;    That's it!
  316.  
  317.         mov    ax, 100h
  318.         ret
  319.  
  320. clk_input    endp
  321.  
  322. ;
  323. ;    clk_output
  324. ;
  325. ;    Sets the real-time clock date and time.
  326. ;
  327. ;    For those who want to do date conversions in the worst
  328. ;    possible way, this is it: the worst possible way.  I
  329. ;    assume that setting the clock is a rare operation, so I
  330. ;    don't care if it takes ten times as long as necessary,
  331. ;    and just want to keep the code size down.
  332. ;
  333.  
  334. clk_output    proc    near
  335.  
  336.         cmp    es:rh_count[di], 6
  337.         je    clk_output_1    ; Only valid requests supported
  338.  
  339.         mov    ax, 800Ah    ; Call it a write fault
  340.         ret
  341.  
  342. clk_output_1:
  343.         les    di, es:rh_address[di]
  344.  
  345. ;    The hard part is converting the number-of-days-since-1-
  346. ;    Jan-80 field to year, month, and day.  We start by
  347. ;    counting years.
  348.  
  349.         mov    ax, es:[di+0]    ; Number of days...
  350.         xor    cx, cx        ; CX = year past 1980
  351.  
  352. clk_output_2:
  353.         mov    bl, cl
  354.         and    bl, 3h
  355.         cmp    bl, 1        ; Carry set if leap year
  356.         mov    bx, 0        ; Don't use XOR 'cause we need CF
  357.         adc    bx, 365        ; Number of days this year
  358.         cmp    ax, bx
  359.         jb    clk_output_3
  360.         sub    ax, bx
  361.         inc    cx
  362.         jmp    clk_output_2
  363. clk_output_3:
  364.  
  365. ;    Now we need to know the month.  Scan through the months
  366. ;    table looking for the first one starting after this
  367. ;    date.  The months table has an extra entry to make sure
  368. ;    this ends properly.  Some incredibly abstruse fiddling
  369. ;    with carries handles leap years.
  370.  
  371.         mov    bx, 2        ; No sense worrying about January
  372.         xor    si, si        ; Holds previous month's start
  373. clk_output_4:
  374.         mov    dl, cl
  375.         and    dl, 3h
  376.         cmp    bx, 4        ; Index for March in table
  377.         adc    dl, 0
  378.         cmp    dl, 1        ; Carry set if leap year and month
  379.                     ;   March or later
  380.         mov    dx, month_date[bx]
  381.         adc    dx, 0        ; Fix up leap year dates
  382.         cmp    ax, dx
  383.         jb    clk_output_5
  384.         mov    si, dx
  385.         inc    bx
  386.         inc    bx
  387.         jmp    clk_output_4
  388. clk_output_5:
  389.         sub    ax, si        ; Day of month (0-based)
  390.         mov    dl, al
  391.         shr    bx, 1        ; Month (1-based)
  392.         mov    dh, bl
  393.  
  394. ;    Now put that all aside for the moment.  We're going to
  395. ;    set the time of day (to nothing useful) to make sure it
  396. ;    doesn't flip the date over while we're updating things.
  397.  
  398.         push    cx
  399.         push    dx
  400.  
  401.         mov    ah, 2h
  402.         int    1Ah        ; Just to get the DST flag
  403.         xor    cx, cx
  404.         xor    dh, dh
  405.         mov    ah, 3h
  406.         int    1Ah
  407.  
  408. ;    Date format needs a little adjusting.  Convert year-1980
  409. ;    to century/year, day to 1-based, and everything to BCD.
  410.  
  411.         pop    dx
  412.         mov    al, dl
  413.         inc    al
  414.         call    tobcd        ; Day of month
  415.         mov    dl, al
  416.         mov    al, dh
  417.         call    tobcd        ; Month
  418.         mov    dh, al
  419.  
  420.         pop    ax        ; Year - 1980
  421.         add    ax, 80
  422.         mov    ch, 19h
  423.         cmp    ax, 100
  424.         jb    clk_output_6
  425.         mov    ch, 20h
  426.         sub    ax, 100
  427. clk_output_6:
  428.         call    tobcd        ; Year within century
  429.         mov    cl, al
  430.  
  431. ;    Phew.  Now we can set the date.
  432.  
  433.         mov    ah, 5h
  434.         int    1Ah
  435.  
  436. ;    Setting the time is a breeze.  Just convert parts of it
  437. ;    to BCD and go.
  438.  
  439.         mov    ah, 2h
  440.         int    1Ah        ; To get DST flag
  441.  
  442.         mov    al, es:[di+5]
  443.         call    tobcd        ; Seconds
  444.         mov    dh, al
  445.         mov    al, es:[di+2]
  446.         call    tobcd        ; Minutes
  447.         mov    cl, al
  448.         mov    al, es:[di+3]
  449.         call    tobcd        ; Hours
  450.         mov    ch, al
  451.  
  452.         mov    ah, 3h
  453.         int    1Ah        ; Set time
  454.  
  455. ;    Done.
  456.  
  457.         mov    ax, 100h
  458.         ret
  459.  
  460. clk_output    endp
  461.  
  462. ;
  463. ;    frombcd
  464. ;
  465. ;    Converts a two-digit BCD number in AL to its binary
  466. ;    equivalent in AX.  Uses AX and BX but preserves all
  467. ;    other registers.
  468. ;
  469.  
  470. frombcd        proc    near
  471.  
  472.         mov    bl, al
  473.         and    al, 0Fh        ; One's digit
  474.         and    bl, 0F0h    ; Ten's digit
  475.         shr    bl, 1
  476.         add    al, bl        ; One's + (ten's * 8)...
  477.         shr    bl, 1
  478.         shr    bl, 1
  479.         add    al, bl        ; ...+ (ten's * 2)
  480.         xor    ah, ah
  481.         ret
  482.  
  483. frombcd        endp
  484.  
  485. ;
  486. ;    tobcd
  487. ;
  488. ;    Converts a binary number in AL to its BCD equivalent.
  489. ;    This is a rotten way to do a conversion, but I don't
  490. ;    care.  Used only when setting the clock, so it's not
  491. ;    time-critical.  AL on entry must be less than 100.
  492. ;
  493.  
  494. tobcd        proc    near
  495.  
  496.         xor    ah, ah
  497. tobcd_loop:
  498.         cmp    al, 10
  499.         jb    tobcd_return
  500.         sub    al, 10
  501.         add    ah, 10h
  502.         jmp    tobcd_loop
  503. tobcd_return:
  504.         or    al, ah
  505.         ret
  506.  
  507. tobcd        endp
  508.  
  509. ;
  510. ;    End of resident part of driver
  511. ;
  512.  
  513. clock_end    equ    $
  514.  
  515. ;
  516. ;    clk_initialize
  517. ;
  518. ;    Driver initialization.  Very little to do here except
  519. ;    set the driver end address.
  520. ;
  521.  
  522. clk_initialize    proc    near
  523.  
  524. ;    Check whether we really have a CMOS clock.
  525.  
  526.         mov    ah, 2h
  527.         stc
  528.         int    1Ah
  529.         jnc    clk_initialize_1
  530.  
  531. ;    No clock.  Some silly person must be trying to install
  532. ;    us on a PC or XT.  We'll show them!
  533.  
  534.         mov    dd_attrib, 0
  535.         mov    es:ri_nunits[di], 0
  536.         mov    word ptr es:ri_endaddr+0[di], 0
  537.         mov    word ptr es:ri_endaddr+2[di], cs
  538.  
  539.         mov    ax, 100h
  540.         ret
  541.  
  542. clk_initialize_1:
  543.         mov    word ptr es:ri_endaddr+0[di], offset clock_end
  544.         mov    word ptr es:ri_endaddr+2[di], cs
  545.  
  546.         mov    ax, 100h
  547.         ret
  548.  
  549. clk_initialize    endp
  550.  
  551. clock        ends
  552.         end
  553.