;
; srvclock.asm  -  From PC Magazine . August '87, pp468-470
;
;    A background task for use on any AT running NETBIOS.  This routine will 
; get and send its date and time information to any node executing 
; NETCLOCK.EXE.  NETCLOCK.EXE then installs the received date and time in the 
; node.  Since SRVCLOCK.EXE reads th AT's CMOS RAM directly, it works only on 
; ATs
;
cr            equ    0dh
lf            equ    0ah

cseg  segment para   public   'code'
net           proc   far
              assume cs:cseg,ds:netdata,ss:stack,es:netdata
              push   ds
              xor    ax,ax
              push   ax
              mov    ax,netdata
              mov    ds,ax
              mov    es,ax
;
; dealocate block containing environment
;
              mov    ah,62h                  ; get program segment prefix
              int    21h                     ;   address
              push   ds                      ; save old data segment
              mov    ds,bx                   ; mov PSP segment to DS  - rsb
              mov    es,ds:02ch              ; move the block address
              mov    ah,49h                  ; containing the enviroment
              int    21h                     ; and deallocate it
              pop    ds                      ; restore DS
              mov    ax,ds                   ;
              mov    es,ax                   ; and ES
;
; netbios add name call (waits for completion)
;
add_name:     mov    command,30h             ; Command 30H adds a neme
              mov    bx,offset mcb           ; point BX at MCB
              int    5ch                     ; call netbios
              cmp    retcode,00h             ; check for success
              je     prep_res                ; prepare for TSR if good 
              mov    dx,offset add_err       ; else write errro message
              jmp    error                   ; name that was not successful
prep_res:
              mov    dx,30h                  ; number of paragraphs for
                                             ; terminate and stay resident
;
; do netbios listen posting into get_clock, then tsr
;
              mov    command,91h             ; Move listen to MCB no wait
              mov    post@,offset get_clock  ; and post at get clock
              mov    post@+2,cs              ; routine
              mov    bx,offset mcb           ; point BX to MCB
              int    5ch                     ; netbios call
              cmp    al,00h                  ; check for good return
              je     go_res                  ; if good, make resident
              mov    dx,offset lsn_err       ; else write error
              jmp    error                   ;
go_res:       mov    ah,31h                  ; DOS terminate and stay
              int    21h                     ; resident
;
; this listen is returned to after a opost into get_clock
;
listen:       mov    command,91h             ; Move listen to MCB
              mov    post@,offset get_clock  ; also posts to clock
              mov    post@+2,cs              ; routine
              mov    bx,offset mcb           ; point BX to block
              int    5ch                     ; NETBIOS call
              sti                            ; restore interrupts
              iret                           ; and return
;
; get date and time to send to requesting node
;
get_clock:    cli                            ; cut off interrupts
              mov    ax,es                   ; ES is restored by network
              mov    ds,ax                   ; use to restore DS
              xor    ax,ax                   ; clear AX
              mov    al,09                   ; 09 = Year
              out    70h,al                  ; Real time clock at 70H
              in     al,71h                  ; returns at 71H
              call   bcd2bin                 ; convert to binary
              add    ax,76ch                 ; add 1900 decimal, only works 
                                             ; until 1999
              mov    year,ax                 ; load buffer with year
                                             
              xor    ax,ax                   ; get month
              mov    al,08                   ;
              out    70h,al                  ;
              in     al,71h                  ;
              call   bcd2bin                 ;
              mov    month,al                ;

              xor    ax,ax                   ; get date of month
              mov    al,07                   ;
              out    70h,al                  ;
              in     al,71h                  ;
              call   bcd2bin                 ;
              mov    day,al                  ;

              xor    ax,ax                   ; get hours
              mov    al,04                   ;
              out    70h,al                  ;
              in     al,71h                  ;
              call   bcd2bin                 ;
              mov    hours,al                ;

              xor    ax,ax                   ; get minutes
              mov    al,02                   ;
              out    70h,al                  ;
              in     al,71h                  ;
              call   bcd2bin                 ;                        
              mov    minutes,al              ;

              xor    ax,ax                   ; get seconds
              mov    al,00                   ;
              out    70h,al                  ;
              in     al,71h                  ;
              call   bcd2bin                 ;
              mov    seconds,al              ;
;
; send clock data to requesting node using a netbios send
;
              mov    buffer@,offset clock    ; point to clock data area
              mov    buffer@+2,ds            ; insure correct segment
              mov    buf_length,8            ; clock takes 8 byees
              mov    command,14h             ; 14H=send and wait
              mov    bx,offset mcb           ; point to MCB
              int    5ch                     ; NETBIOS

hangup:       mov    command,12h             ; 12H=hangup
              mov    bx,offset mcb           ; point BX
              int    5ch                     ; NETBIOS call

              mov    si,offset fill_name     ; fill name=listen any
              mov    di,offset callname      ; move to call name
              mov    cx,0fh                  ; names are 16 bytes
    repnz     movsb                          ;
              jmp    listen                  ; back to waiting for call
;
; error message, failed before coming resident
;
error:        mov    ah,09h                  ; display error message
              int    21h                     ; if routine fails before 
                                             ; becoming resident
              mov    command,12h             ; hang up session if any
              mov    bx,offset mcb           ; point BX
              int    5ch                     ; NETBIOS call
quit:         ret                            ;

net           endp

;
; procedure to convert time as delivered from CMOS clodk (binary coded decimal)
; to binary (as used by DOS function calls and NETCLOCK) (returns in AL)
;
bcd2bin       proc   near
              push   ax                      ; save contents
              and    al,0f0h                 ; clear out low nibble
              shr    al,1                    ; divide by 16
              shr    al,1                    ;    so that hi nibble
              shr    al,1                    ;    is now in
              shr    al,1                    ;    lo position
              mov    bl,al                   ; hold in al
              shl    al,1                    ; multiply by ten, two here
              shl    al,1                    ;    four
              shl    al,1                    ;    eight
              add    al,bl                   ;    nine
              add    al,bl                   ;    ten
              pop    bx                      ; get previous AX contents
              and    bl,0fh                  ; clear out hi nibble
              add    al,bl                   ; add to total
              ret
bcd2bin       endp

cseg          ends
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                          message data
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
netdata segment para public 'data'
add_err       db     cr,lf,'"add name" command failed',cr,lf,'$'
lsn_err       db     cr,lf,'"listen" command failed',cr,lf,'$'
fill_name     db     '*               '
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                      message control area
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
clock         equ    $
year          dw     0000h
month         db     00h
day           db     00h
hours         db     00h
minutes       db     00h
seconds       db     00h
hundredths    db     00h
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                      message control block
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mcb           equ    $
command       db     00h
retcode       db     00h
lsn           db     00h
num           db     00h
buffer@       dw     0000h
              dw     0000h
buf_length    dw     0000h
callname      db     '*               '
our_name      db     'srv_clock       '
rto           db     00h
sto           db     00h
post@         dw     0000h
              dw     0000h
adapter_num   db     00h
cmd_stat      db     00h
reserve       db     14 dup(0)

netdata       ends
stack         segment para stack 'stack'
              db     32 dup('stack   ')
stack         ends
              end

