              .title   Time_Booster
              .ident   "Version 1.0"
; 
;             Copyright 2001 Compaq Computer Corporation
;
;	      Adapted to OpenVMS Alpha 23-mar-1995
;             LINK/SYSEXE  to link against the system
;
              .library "sys$share:lib.mlb"   ; Include the system macro library
              $clidef
              $ssdef

              .macro   test_ret    ?l
              blbs     r0,l
              $exit_s  r0
l:
              .endm    test_ret

              .psect   string,noexe, nowrt, quad
ann:          .ascid   "%TBO-I-IDENT, OpenVMS Time Booster Rev 1.0"
res_ann:      .ascid   "%TBO-I-RESET, Default ticklength restored"
cntr_info:    .ascid   "%TBO-I-INFO,  Systemtime: !AS!/              Timeadjust: !UL!/              Ticklength: !UL"
noalq:        .ascid   "%TBO-E-NOTALLQUAL, Not all qualifiers are specified"
res_entity:   .ascid   "reset"
inf_entity:   .ascid   "info"
del_entity:   .ascid   "delta"
rang_entity:  .ascid   "range"
dir_entity:   .ascid   "direction"
dir_det:      .ascii   "B"
addend:       .long    0
              
              .page
              .psect   data  ,noexe,   wrt, quad
lock_adr:     .address lock_start
              .address lock_end
deflen:       .blkl    1
numargs:      .long    0
direc_s:      .long    1                         ; Direction is forward
range_s:      .long    0
delta_s:      .long    0
ticklength:   .blkl    1
timeadjust:   .blkl    1
systick:      .blkl    1
remainder:    .blkl    1
multipl:      .blkl    1
product:      .blkq    1
systime:      .blkq    1
systime$:     .ascid   "xx-xxx-xxxx xx:xx:xx.xx" 
out_desc:     .long    132
              .address faobuf
faobuf:       .blkb    132
cvt_desc:     .word    8
              .word    270
              .address cvtbuf
cvtbuf:       .blkb    8

              .page
              .psect   code  ,  exe, nowrt, quad

              .entry main,^m<>

              pushal   ann                       ; Push descriptor addres
              calls    #1,g^lib$put_output       ; Let every one whats running
              $lkwset_s inadr=lock_adr           ; Lock the privileged code in the working set
              test_ret                           ; Check the return value
              pushal   res_entity
              calls    #1,g^cli$present          ; Do we want to reset?
              cmpl     r0,#cli$_present          ; If option is reset then
              bneq     10$
              $cmkrnl_s routin=reset_hwclk       ; Initiate reset routine
              test_ret                           ; Check the return value
              pushal   res_ann                   ; Push success message
              calls    #1,g^lib$put_output       ; Cry it out!
              brw      20$
10$:          bsbw     scan_cli                  ; See if there is something to mess around whith?
              tstl     numargs                   ; No qualifiers?
              beql     20$                       ; Then there is only one item to check left. 
              cmpl     #3, numargs               ; Are there three items 
              beql     22$                       ; If not, then exit
              pushal   noalq
              calls    #1,g^lib$put_output
              brb      30$
22$:          bsbw     calc_ticks                ; Calculate needed correction
20$:          pushal   inf_entity
              calls    #1,g^cli$present          ; Do we want info?
              cmpl     r0,#cli$_present          ; If option is info then
              bneq     30$
              bsbw     hwclk_info                ; try to find out the current settings
30$:          $exit_s                            ; Initiate image rundown
              ret


;              .entry hwclk_info,^m<>

hwclk_info:   $cmkrnl_s routin= copy_info          
              test_ret                           ; Check the return value
              $asctim_s timlen=deflen, -         ; Convert quad word to system time string
                       timbuf=systime$, -
                       timadr=systime, -
                       cvtflg=#0
              $fao_s   ctrstr=cntr_info, -       ; Make everything readable and
                       outbuf=out_desc, -        ; store it in a buffer
                       p1=#systime$, -
                       p2=timeadjust, -
                       p3=ticklength
              pushal   out_desc
              calls    #1,g^lib$put_output       ; Cry it out!
              rsb


;              .entry scan_cli,^m<>

scan_cli:     clrl     numargs
              pushal   dir_entity               
              calls    #1,g^cli$present          ; Test the precense of the direction qualifier
              cmpl     r0,#cli$_present          ; Which way do we want to travel?
              bneq     110$                      ; No direction?!, check the user for another errors!
              incl     numargs                   ; Increment the parameter counter
              pushal   cvt_desc
              pushal   dir_entity
              calls    #2,g^cli$get_value        ; Return the value assigned to the qualifier
              moval    cvtbuf,r1
              cmpb     dir_det,(r1)              ; If direction is backward
              bneq     110$
              movl     #-1,direc_s               ; Direc_s is negative
110$:         pushal   del_entity                ; Check next entity
              calls    #1,g^cli$present          ; Test the presence of the delta qualifier
              cmpl     r0,#cli$_present          ; How many seconds are involved
              bneq     115$                      ; And no delta? What are we doing?
              incl     numargs
              movl     #8,cvt_desc               ; Restore buffer size
              pushal   cvt_desc                  ; Let the return value set up the descriptor
              pushal   cvt_desc
              pushal   del_entity
              calls    #3,g^cli$get_value        ; Return the delta value
              pushal   delta_s
              pushal   cvt_desc
              calls    #2,g^ots$cvt_tu_l         ; Convert delta string to unsigned long word
              test_ret
115$:         pushal   rang_entity               ; Check next entity
              calls    #1,g^cli$present          ; Test the presence of the range qualifier
              cmpl     r0,#cli$_present          ; How many seconds are involved
              bneq     120$                      ; Even no range, we can better exit this routine
              incl     numargs
              movl     #8,cvt_desc               ; Restore buffer size
              pushal   cvt_desc                  ; Let the return value set up the descriptor
              pushal   cvt_desc
              pushal   rang_entity
              calls    #3,g^cli$get_value        ; Return the range value
              pushal   range_s
              pushal   cvt_desc
              calls    #2,g^ots$cvt_tu_l         ; Convert delta string to unsigned long word
              test_ret
120$:         movl     #ss$_normal,r0            ; Restore the success return value for sys$cmkrnl
              rsb

;              .entry calc_ticks,^m<>

calc_ticks:   $cmkrnl_s routin=get_systick       ; Retrieve systick
              movl #10000000,r1                  ; Load clock resolution (1/100ns)
              movl range_s,r2                    ; Load range in seconds
              movl systick,r3                    ; Load exe$gl_systick
              divl2 r3,r1                        ; r1 contains number of ticks required to adjust time
              mull2 r2,r1                        ; Formula:(1E7*range/systick)
              movl r1,timeadjust                 ; Save it

              movl delta_s,r4                    ; Load delta value
              movl direc_s,r5                    ; Load direction
              mull2 r5,r4                        ; calc direction
              addl2 r2,r4                        
              movl  r4,multipl                   ; Store r4
              pushal product                     ; Push the arguments
              pushal addend
              pushal systick
              pushal multipl
              calls  #4,g^lib$emul               ; Perform an 64 bit multiplication
              pushal remainder
              pushal ticklength
              pushal product
              pushal range_s
              calls  #4,g^lib$ediv               ; Formula: ((range+(delta*direc))*systick/range) => new ticklength
              $cmkrnl_s routin=store_ticks       ; Store calculated values in hwclk data structures
              rsb

              .page
              .psect   prcode,  exe, nowrt       ; This psect contains code which runs at an elevated ipl

              .entry        reset_hwclk,^m<>

lock_start:                                      ; and is locked in the working set
              lock     lockname=hwclk            ; Lock hardware clock data structures
              clrl     g^exe$gl_timeadjust       ; Disable clock adjustment
              movl     g^exe$gl_systick,g^exe$gl_ticklength ; Restore standard tick
              unlock   lockname=hwclk, newipl=#0 ; Release the hardware clock lock
              movl     #ss$_normal,r0            ; Restore the success return value for sys$cmkrnl
              ret

              .entry        copy_info,^m<>

              lock     lockname=hwclk            ; Lock hardware clock data structures
              movl     g^exe$gl_timeadjust, timeadjust ; Save timeadjust value in order to release lock as quick as possible
              movl     g^exe$gl_ticklength, ticklength ; Do the same with the ticklength value
              movq     g^exe$gq_systime, systime ; Even system time is saved for whatever the purpose may be
              unlock   lockname=hwclk, newipl=#0 ; Release the hardware clock lock
              movl     #ss$_normal,r0            ; Restore the success return value for sys$cmkrnl
              ret

              .entry        get_systick,^m<>

              lock     lockname=hwclk            ; Lock hardware clock data structures
              movl     g^exe$gl_systick,systick  ; Copy systick value
              unlock   lockname=hwclk, newipl=#0 ; Release the hardware clock lock
              movl     #ss$_normal,r0            ; Restore the success return value for sys$cmkrnl
              ret

              .entry        store_ticks,^m<>

              lock     lockname=hwclk            ; Lock hardware clock data structures
              movl     timeadjust, g^exe$gl_timeadjust ; Store timeadjust value
              movl     ticklength, g^exe$gl_ticklength ; Store new ticklength value
              unlock   lockname=hwclk, newipl=#0 ; Release the hardware clock lock
              movl     #ss$_normal,r0            ; Restore the success return value for sys$cmkrnl
              ret
lock_end:
              .end main
