Article 157579 of comp.os.vms:
A few weeks ago I posted a utility for changing to summer/wintertime.
Jim McKinney discovered some potential problems regarding clock cycle
lengths on an Alpha. With thanks too Jim, I hereby post the modified
code.

================================ REPOST ========================================

TBO (Time BOoster) was given to us by Digital (without any support !!!)
We use this utility on our VAXes and Alpha's without any problems sofar.

Mod's:
------
Jim McKinney discovered a bug in the source code concearning the clock cycle
length. Alpha's use a variable clock speed, and Jim modified the code to cope
with this. (Full version of his comments is attached with the code)

 Thanks Jim.

--------------------------------------------------------------------------------

How to build TBO:

Underneath you find 3 files; TBO.CLD, TBO.HLP, and TBO.MAR

1. Modify the line "image 'Dev:[Dir]'TBO" in TBO.CLD to point to the
   device and directory were you will put TBO.EXE.

2. If wanted put TBO.HLP in a helplibrary

3. Compile and link TBO.MAR (see header for info) and copy the image
   the device and directory mentioned unde 1.


Good luck, but..... Use TBO at your own risk............

=========== Start of TBO.CLD ============== Cut Here ===========================
define verb TBO
image 'Dev:[Dir]'TBO
  noparameters
  qualifier RESET	nonnegatable
  qualifier INFO	nonnegatable
  qualifier DIRECTION	nonnegatable, value(required, type=DIR_TYPE)
  qualifier DELTA	nonnegatable, value(required, type=$NUMBER)
  qualifier RANGE	nonnegatable, value(required, type=$NUMBER)

  disallow		RESET and (DELTA or RANGE or DIRECTION)
  disallow		DELTA and not (RANGE and DIRECTION)
  disallow		RANGE and not (DELTA and DIRECTION)
  disallow		DIRECTION and not (DELTA and RANGE)

define type DIR_TYPE
  keyword BACKWARD
  keyword FORWARD
============= End of TBO.CLD ============== Cut Here ===========================


=========== Start of TBO.HLP ============== Cut Here ===========================
1 TBO

  TBO (Time Booster) is a OpenVMS VAX and AXP utility which allows
  privileged users to gradualy move the OpenVMS time back- or forwards.
  This utility is ment to be used when switching from summer- to
  wintertime or vica versa.


             Supplied by Digital without support !!!!


  Syntax:

    $ TBO /Qualifiers


2 Parameter

  TBO has no parameters


2 Example

    $ TBO /INFO
    %TBO-I-IDENT, VAX/VMS Time Booster Rev 1.0
    %TBO-I-INFO,  Systemtime:  2-APR-1996 10:28:05.58
                  Timeadjust: 0
                  Ticklength: 9765

    $ TBO /DIRECTION=BACKWARD /RANGE=14400 /DELTA=3600 /INFO
    %TBO-I-IDENT, VAX/VMS Time Booster Rev 1.0
    %TBO-I-INFO,  Systemtime:  2-APR-1996 10:29:34.30
                  Timeadjust: 14745600
                  Ticklength: 7323

  This command will move the clock 1 hour back over a period of
  4 hours (summertime -> wintertime). /INFO will display the
  current time, the adjustment duration, and the reduced ticklength.

  Subsequent TBO /INFO commands will display the TBO progress (i.e.
  the Timeadjust value will reduce slowly to zero).

  Once TBO is active it can be stopped using the TBO /RESET command.

    $ TBO /RESET /INFO
    %TBO-I-IDENT, VAX/VMS Time Booster Rev 1.0
    %TBO-I-RESET, Default ticklength restored
    %TBO-I-INFO,  Systemtime:  2-APR-1996 10:34:17.57
                  Timeadjust: 0
                  Ticklength: 9765


2 /DIRECTION

  Format:

    TBO /DIRECTION=Keyword /RANGE=Number /DELTA=Number [/INFO]

  /DIRECTION is used to specify whether the time adjustment is made
  for summertime (/DIRECTION=FORWARD) or for wintertime (/DIRECTION=
  BACKWARD).

  This qualifier must be used together with /DELTA and /RANGE, /INFO
  is optional.


2 /RANGE

  Format:

    TBO /DIRECTION=Keyword /RANGE=Number /DELTA=Number [/INFO]

  /RANGE is used to specify the adjustment duration in seconds.
  So /RANGE=14400 specifies a 4 hour period during which the
  clock will be adjusted in the specified direction.

  This qualifier must be used together with /DIRECTION and
  /DELTA, /INFO is optional.


2 /DELTA

  Format:

    TBO /DIRECTION=Keyword /RANGE=Number /DELTA=Number [/INFO]

  /DELTA is used to specify the number of seconds the clock has to
  be adjusted. So /DELTA=3600 is used to move the clock 1 hour back-
  or forward (depends on the value of /DIRECTION). The duration of
  the adjustment is specified by /RANGE.

  This qualifier must be used together with /DIRECTION and
  /RANGE, /INFO is optional.


2 /RESET

  Format:

    TBO /RESET

  This command (no other qualifiers, except /INFO, are allowed) resets
  the clock update action. Whenever a TBO command is issued to change
  the time, 2 internal OpenVMS memory locations (EXE$GL_TICKLENGTH and
  EXE$GL_TIMEADJUST) are modified. /RESET forces the original values
  into the memory locations, therby stopping the adjustment action.


2 /INFO

  Format:

    TBO /INFO

  Using /INFO will display the current system time, and the contents
  of the locations EXE$GL_TICKLENGTH and EXE$GL_TIMEADJUST. The first
  memory location specifies the number of hardware clock ticks which
  make up a 10 msec unit, the 2nd location specifies the number of
  hardware clockticks which have to pass before the 1st loctaion is
  reseted to it's original value.

============= End of TBO.CLD ============== Cut Here ===========================


Jim McKinney's comments and suggestions on TBO
----------------------------------------------

From:	ITTPUB::IN%"mckinneyj@cpva.saic.com"  4-OCT-1996 21:24:07.59
To:	veen@nl101.ittpub.nl
CC:	
Subj:	RE: [Macro Code]: How to change time backwards?

From cpva.saic.com!MCKINNEYJ  Fri Oct  4 20:37:58 1996 remote from sun4nl
Received: from sun4nl by ittpub.nl (DECUS UUCP ///1.3-1/2.5/);
          Fri,  4 Oct 96 21:24:10 WET
Received: from cpmx.mail.saic.com by sun4nl.NL.net (5.65b/NLnet-3.4)
          id AA29174; Fri, 4 Oct 1996 20:37:58 +0200
Return-Path: <MCKINNEYJ@cpva.saic.com>
Received: from cpva.saic.com by cpmx.mail.saic.com; Fri, 4 Oct 96 11:37:49 -0700
Date: Fri, 4 Oct 1996 11:36:43 -0700 (PDT)
From: MCKINNEYJ@cpva.saic.com
To: veen@nl101.ittpub.nl
Message-Id: <961004113643.2023b1bd@cpva.saic.com>
Subject: RE: [Macro Code]: How to change time backwards?

Hans -
    You posted some code on 15-Aug-1996... TBO. It's a slick little utility;

>X-News: cpgtsa comp.os.vms:124161
>From: veen@nl101.ittpub.nl (Hans van Veen - ITT Gouden Gids)
>Subject:[Macro Code]: How to change time backwards?
>Date: 15 Aug 96 12:00:19 WET+0100
>Message-ID:<1996Aug15.120019@ittpub>

    however, it has a dependency on the clock cycle of the VAX and gives
    incorrect results on Alphas. It will not compute the Timeadjust period
    correctly since the Alpha's clock cycle is different than the VAX's.
    You can check this out by issueing the TBO command for some RANGE
    (perhaps an hour) and specifying a 0 value for the DELTA. If you time
    it with your watch you'll see that it isn't an hour (but on the VAX it
    is). Try a command like TBO/DIRE=FORW/RANGE=3600/DELT=0/INFO. For an
    Alpha (with a clock cycle of 1024) you would expect a Timeadjust value
    reported back of 3686400 (1024cycles*60min*60sec). I've modified the
    code to (I believe) handle the Alpha's potentially variable clock speed
    and have attached the code below. Check it out and see what you think.
    Look for the string "ALPHA" in the code to see all the modifications,
    as I've implemented them all as "ifdefs". If you agree, then perhaps
    you should post the updated version  to comp.os.vms (or infovax) for
    those that have Alphas. Anyway, let me know what you think.
- Jim
--
Jim McKinney:  mckinneyj@cpva.saic.com | (619)458-2075 | SAIC, Campus Point

=========== Start of TBO.MAR ============== Cut Here ===========================

	.title	Time_Booster
	.ident	"Version 2.0"

; VAX has a 10millisecond clock (a tick). ALPHA has an interval timer that
; is determined by the value in the hardware restart parameter block (HWRPB)
; at offset HWRPB$IQ_CLOCK_INT_FREQ. This value will minimally be 1000 and
; and initially all ALPHA systems will generate 1024 interupts (ticks) per
; second.
;
; Note that we'll use the following:
; VAX & ALPHA:
;  EXE$GL_SYSTICK - standard clock tick length 
;  EXE$GL_TICKLENGTH - increment added to clock at each hardware clock interrupt
;  EXE$GL_TIMEADJUST - number of ticks to apply EXE$GL_TICKLENGTH
; ALPHA:
;  EXE$GPL_HWRPB_L - hardware restart parameter block
;  HWRPB$IL_CLOCK_FREQ_L - offset to clock interrupt frequency

;
; define ALPHA if R22 is a register and not a symbol
;
	.ntype  ...IS_IT_ALPHA,r22		;Get the type of R22
	...IS_IT_ALPHA = <...IS_IT_ALPHA@-4&^XF>-5
	.iif eq,...IS_IT_ALPHA, ALPHA=1

	.library "sys$share:lib.mlb"
	.if not_defined, ALPHA			; VAX only
	.link "sys$system:sys.stb" /selective_search
	.endc					; AXP use LINK/SYSEXE=SELECTIVE

	$clidef					; CLI codes
	.if defined, ALPHA
	$hwrpbdef				; hardware codes
	.endc
	$ssdef					; system service status codes

	.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 2.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"
	.align   long
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
		.if defined, ALPHA
clockfreq:	.blkl    1
		.endc
systick:	.blkl    1
remainder:	.blkl    1
multipl:	.blkl    1
		.align   quad
product:	.blkq    1
systime:	.blkq    1
systime$:	.ascid   "xx-xxx-xxxx xx:xx:xx.xx"
		.align   long
out_desc:	.long    132
		.address faobuf
faobuf:		.blkb    132
		.align   long
cvt_desc:	.word    8
		.word    270
		.address cvtbuf
cvtbuf:		.blkb    8

	.page
	.psect	code, exe, nowrt, quad

	.entry main,^m<>
	pushal   ann		        ; Push descriptor address
	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



hwclk_info:   .iif defined, ALPHA, .jsb_entry
	$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



scan_cli:    .iif defined, ALPHA, .jsb_entry output=r0, preserve=r1
	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 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


calc_ticks:   .iif defined, ALPHA, .jsb_entry preserve=<r1,r2,r3,r4,r5>
	$cmkrnl_s routin=get_systick	; Retrieve systick
	.if defined, ALPHA		; If AXP Derive the clock resolution
	movl clockfreq,r1		;;  Load ticks per second
	movl systick,r3			;;  Load standard clock tick length
	mull2 r3,r1			;;  Formula: (freq*systick)
	.iff				; Else
	movl #10000000,r1		; If VAX Load clock resolution (1/100ns)
	.endc
	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 a 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
	.if defined, ALPHA
	.call_entry max_args=0,home_args=false,label=reset_hwclk,output=r0
	.iff
	.entry	reset_hwclk,^m<>
	.endc
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


	.if defined, ALPHA
	.call_entry max_args=0,home_args=false,label=copy_info,output=r0
	.iff
	.entry	copy_info,^m<>
	.endc
	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


	.if defined, ALPHA
	.call_entry max_args=0,home_args=false,label=get_systick,output=r0
	.iff
	.entry	get_systick,^m<>
	.endc
	lock     lockname=hwclk		; Lock hardware clock data structures
	movl     g^exe$gl_systick,systick ; Copy systick value
	.if defined, ALPHA		; Copy clock interrupt frequency
					; and adjust for it's scaling
	; the clock interrupt frequency rate is given to the system in the
	; HWRPB. The value is a scaled up value (*4096) of the real ticks
	; per second value.
	; (VMS Source Listings - [V62.SYS.LIS]TIMESCHDL_MIN.LIS)
	movl     exe$gpl_hwrpb_l, r0
	ashl     #-12, hwrpb$il_clock_int_freq_l(r0), clockfreq
	.endc
	unlock   lockname=hwclk, newipl=#0 ; Release the hardware clock lock
	movl     #ss$_normal,r0		; Restore the success return value for sys$cmkrnl
	ret


	.if defined, ALPHA
	.call_entry max_args=0,home_args=false,label=store_ticks,output=r0
	.iff
	.entry	store_ticks,^m<>
	.endc
	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

============= End of TBO.MAR ============== Cut Here ===========================

-- 

Cheers, Hans

      Hans van Veen                       Email  : hvveen@goudengids.nl
      ITT Gouden Gids IS/OS                        hansv@ittpub.nl
      Hoekenrode 1                        Phone  : +31 (0)20 5676797
      1102 BR  AMSTERDAM, Netherlands     Fax    : +31 (0)20 6965480



