Subject: Standalone TOY clock setting program (#151)
Index:	sys/pdpstand/{Makefile,toyset.s} 2.11BSD

Description:
	The TOY (Time Of Year) clock of a KDJ-11E (11/93 or 11/94)
	is difficult/confusing to change from the console monitor,
	especially if the manual is missing or not at hand.

Repeat-By:
	Observation/experience.

Fix:
	Below is a new program which can be loaded by /boot (or by the
	boot program on a bootable tape) and used to set the TOY clock.

	The TOY clock must be set according to GMT (well, UCT - same
	thing;-)).  The /etc/zoneinfo files are used by the system
	to take into account the timezone and daylight savings time.

	The format of the date string expected by 'toyset' is the same
	as the date(1) command:

			YYMMDDHHmm[.ss]

	See the man page for date(1) for additional details.

	After unshar'ing the file below (which includes a patch to the
	pdpstand/Makefile as well as the source to the 'toyset' program)
	you should:

			cd /sys/pdpstand
			patch < /tmp/c
			make toyset
			install -s toyset /toyset

	To run the program you specify 'xx(dd,0)toyset' at the ':' prompt
	of 'boot':

		: ra(0,0)toyset

	The program prints out the current TOY setting and then prompts:

	Current TOY: 9112160736.19
	Toyset> 

	At this point you enter a string like this:

			9308241330.45

	which will set the TOY to "August 24, 1993 13:30:45"

	Any invalid input causes the program to reprompt.  If the date
	string is accepted the 'toyset' program exits and returns control
	back to 'boot' at which point you can load the system.

	Only the 11/93 and 11/94 have the TOY clock.  Running 'toyset'
	other pdp-11s simply gives the error message:

		Cputype is not 93 or 94.  No TOY present

	and returns to 'boot'.

	Enjoy!
========================cut here=========================================
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	/tmp/c
#	/sys/pdpstand/toyset.s
# This archive created: Tue Aug 24 20:46:24 1993
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f '/tmp/c'
then
	echo shar: "will not over-write existing file '/tmp/c'"
else
sed 's/^X//' << \SHAR_EOF > '/tmp/c'
X*** /sys/pdpstand/Makefile.old	Wed Mar 18 00:21:44 1992
X--- /sys/pdpstand/Makefile	Sat Aug 21 20:58:02 1993
X***************
X*** 31,37 ****
X  	ht.o tm.o ts.o tmscp.o \
X  	xp.o rk.o rl.o br.o hk.o si.o ra.o
X  
X! ALL=	mtboot boot mkfs restor icheck maketape
X  
X  .c.o:
X  	cc ${CFLAGS} -c $*.c
X--- 31,37 ----
X  	ht.o tm.o ts.o tmscp.o \
X  	xp.o rk.o rl.o br.o hk.o si.o ra.o
X  
X! ALL=	mtboot boot mkfs restor icheck maketape toyset
X  
X  .c.o:
X  	cc ${CFLAGS} -c $*.c
X***************
X*** 119,127 ****
X  icheck: srt0.o conf.o libsa.a icheck.o
X  	ld -o $@ srt0.o conf.o $@.o libsa.a -lc
X  
X- 
X  maketape: maketape.c
X  	cc -o $@ maketape.c
X  
X  tags: FRC
X  	rm -f tags
X--- 119,129 ----
X  icheck: srt0.o conf.o libsa.a icheck.o
X  	ld -o $@ srt0.o conf.o $@.o libsa.a -lc
X  
X  maketape: maketape.c
X  	cc -o $@ maketape.c
X+ 
X+ toyset: toyset.o srt0.o conf.o libsa.a
X+ 	ld -o $@ srt0.o conf.o $@.o libsa.a -lc
X  
X  tags: FRC
X  	rm -f tags
SHAR_EOF
fi
if test -f '/sys/pdpstand/toyset.s'
then
	echo shar: "will not over-write existing file '/sys/pdpstand/toyset.s'"
else
sed 's/^X//' << \SHAR_EOF > '/sys/pdpstand/toyset.s'
XTOYCSR	= 177526
X
X/ August 21, 1993 - Steven M. Schultz (sms@wlv.iipo.gtegsc.com)
X/ This is a standalone program which is used to set the TOY (Time Of Year)
X/ clock on a PDP-11/93 or 11/94.  If this program is run on other than a
X/ 93 or 94 an error is printed and the program 'exits' back to the boot
X/ runtime.
X/
X/ The current date is printed (in the same format used to enter the new date)
X/ and the prompt "Toyset> " is displayed.  At that time a string of the form:
X/ YYMMDDHHMM[.SS]\n is entered.  The seconds "SS" are optional, if not entered
X/ the seconds will be set to 0.  Any invalid input string simply loops back
X/ to the top of the program.
X/
X/ To not change the date and time simply hit a return and the program will
X/ exit, returning control to 'boot'.
X
X	.globl	_main, csv, cret, _printf, _gets, _exit, _cputype, _module
X
X_main:
Xmain:
X	jsr	r5,csv			/ srt0.o sets up a C frame...
X
X	jsr	pc,init			/ check cpu type and display current TOY
X
X	clrb	line			/ init buffer
X	mov	$line,-(sp)
X	jsr	pc,_gets		/ get input from user
X	tst	r0
X	bgt	1f
Xleave:
X	jsr	pc,_exit		/ exit on error
X1:
X	tstb	line			/ did we get anything?
X	beq	leave			/ nope - go exit
X
X	clr	r3			/ clear '.' seen flag
X	mov	$line,r4		/ point to input data
X2:
X	movb	(r4)+,r0
X	beq	1f			/ end of string - go validate it
X	cmpb	r0,$'.			/ are there seconds present?
X	bne	2b			/ not yet - go try another byte
X	clrb	-1(r4)			/ zap '.' - separating two parts of date
X	mov	r4,r3			/ set seconds present flag
X	tstb	(r3)+			/ must have two...
X	beq	main			/ and only two...
X	tstb	(r3)+			/ characters after...
X	beq	main			/ the '.'...
X	tstb	(r3)			/ followed by a ...
X	bne	main			/ null character.
X1:
X	clrb	seconds			/ assume no seconds (start of minute)
X	tst	r3			/ do we have seconds?
X	beq	nosec			/ no - br
X	cmpb	-(r3),-(r3)		/ back up to beginning of seconds string
X	mov	r3,-(sp)
X	jsr	pc,atoi2		/ convert two digits to binary
X	tst	(sp)+
X	cmp	r0,$59.			/ range check
X	bhi	main			/ error - go back to top
X	movb	r0,seconds		/ save for later
Xnosec:
X	sub	$line+1,r4		/ number of characters in date string
X	cmp	r4,$10.			/ _must_ have *exactly* "YYMMDDhhmm"
X	bne	main			/ back to the top on error
X	mov	$line,r4		/ point to start of date string
X	mov	r4,-(sp)
X	jsr	pc,atoi2		/ convert 2 digits to binary (year)
X	tst	(sp)+
X	cmp	r4,$-1			/ error?
X	beq	main			/ yes - go back to top
X	cmp	r0,$69.			/ before [19]69?
X	bgt	1f			/ no - it's a 1970-1999 year - br
X	add	$100.,r0		/ 21st century and 11s are still around!
X1:
X	movb	r0,year			/ save year for later
X	cmpb	(r4)+,(r4)+		/ skip two digits, move to month
X	mov	r4,-(sp)
X	jsr	pc,atoi2		/ convert month to binary
X	tst	(sp)+
X	cmp	r0,$12.			/ range check
X	bhi	main			/ back to top on too high
X	cmpb	(r4)+,(r4)+		/ move on to day of month
X	movb	r0,month		/ save month for later
X	beq	main			/ can't have a month 0
X	mov	r4,-(sp)
X	jsr	pc,atoi2		/ convert day of month to binary
X	tst	(sp)+
X	movb	month,r1
X	cmpb	r0,Mtab-1(r1)		/ crude check (no leap year case)
X	bhi	main			/ on day of month
X	movb	r0,day			/ save the day for later
X	cmpb	(r4)+,(r4)+		/ move along to hours of day
X	mov	r4,-(sp)
X	jsr	pc,atoi2		/ convert hours of day to binary
X	tst	(sp)+
X	cmp	r0,$23.			/ can't have more than 23 hours
X	bhi	main			/ but 00 is ok (midnight)
X	movb	r0,hours		/ save hours for later
X	cmpb	(r4)+,(r4)+		/ move over to minutes
X	mov	r4,-(sp)
X	jsr	pc,atoi2		/ convert minutes to binary
X	tst	(sp)+
X	cmp	r0,$59.			/ can't have more than 59 minutes
X	bhi	main			/ back to top on out of range error
X	movb	r0,minutes		/ save for later
X
X/ need to compute the "day of week".  why the TOY clock couldn't figure
X/ this out (or do without) itself i don't know.
X
X	jsr	pc,t2dow		/ find out "day of week"
X	movb	r0,dow			/ save for later
X
X/ now we have to convert the binary data to BCD.  We needed (or preferred)
X/ the binary form for ease of range checking but the TOY wants BCD.  Besides
X/ i like to improve my typing skills ;-)
X
X	 movb	seconds,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+1		/ seconds
X
X	 movb	minutes,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+2		/ minutes
X
X	 movb	hours,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+3		/ hours
X
X	 movb	dow,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+4		/ day of week
X
X	 movb	day,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+5		/ day of month
X
X	 movb	month,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+6		/ month of year
X
X	 movb	year,r1
X	 jsr	pc,tobcd
X	 movb	r0,bcd+7
X
X/ Now initialize the TOY by sending the 'recognition' sequence.  We have
X/ to inline this because immediately after the recognition sequence must
X/ come the 'write' of data - a 'read' to save the contents of the CSR
X/ would tell the TOY we're reading data.  *sigh*
X
X	tst	*$TOYCSR		/ strobe the clock register
X	clr	-(sp)			/ save previous high byte of register
X	movb	*$TOYCSR+1,(sp)		/ only bit 8 belongs to TOY!
X	bic	$1,(sp)			/ make sure bit 8 (TOY bit) is clear
X	mov	$2,r2			/ number of double words to send clock
X1:
X	mov	$35305,r0		/ first word of recognition code
X	jsr	pc,toyload		/ send it to clock
X	mov	$56243,r0		/ second word
X	jsr	pc,toyload		/ send it
X	sob	r2,1b			/ do the whole thing twice
X
X/ Now write the data to the TOY without an intervening 'tst' or 'movb'
X/ to the CSR.
X
X	mov	bcd,r0			/ first two bytes
X	jsr	pc,toyload
X	mov	bcd+2,r0		/ bytes 3 and 4
X	jsr	pc,toyload
X	mov	bcd+4,r0
X	jsr	pc,toyload		/ bytes 5 and 6
X	mov	bcd+6,r0
X	jsr	pc,toyload		/ bytes 7 and 8
X
X	tst	(sp)+			/ clean stack now, we're done
X	clr	r0			/ "exit" status.  ha! ;-)
X	jsr	pc,_exit
X
X/ Check the cpu type - only the 93 and 94 have a TOY.  Then initialize
X/ the TOY and read the current date.  Convert the date into printable
X/ form and print it out along with the prompt.
X
Xinit:
X	cmp	_cputype,$93.
X	beq	1f
X	cmp	_cputype,$94.
X	beq	1f
X	mov	$errmsg1,-(sp)
X	jsr	pc,_printf
X	mov	$1,r0
X	jsr	pc,_exit
X1:
X	jsr	pc,initoy		/ init the TOY clock
X	mov	$bcd,-(sp)		/ buffer for the date
X	jsr	pc,_gettoy		/ read the TOY
X	tst	(sp)+
X	mov	$timbuf,r3		/ where to put printable form of date
X	clr	r1
X	bisb	bcd+7,r1		/ year in bcd
X	jsr	pc,bcd2msg
X	movb	bcd+6,r1		/ month in bcd
X	jsr	pc,bcd2msg
X	movb	bcd+5,r1		/ day of month in bcd
X	jsr	pc,bcd2msg
X	movb	bcd+3,r1		/ hour of day in bcd
X	jsr	pc,bcd2msg
X	movb	bcd+2,r1		/ minute of hour in bcd
X	jsr	pc,bcd2msg
X	movb	$'.,(r3)+
X	movb	bcd+1,r1		/ seconds of minute in bcd
X	jsr	pc,bcd2msg
X	mov	$timmsg,-(sp)
X	jsr	pc,_printf
X	tst	(sp)+
X	rts	pc
X
X/ convert two bytes of ascii pointed to by 2(sp) into a binary number.
X/ the return value is in r0.
X
Xatoi2:
X	movb	*2(sp),r1
X	inc	2(sp)
X	movb	*2(sp),r0
X	sub	$'0,r1
X	sub	$'0,r0
X	mul	$10.,r1
X	add	r1,r0
X	rts	pc
X
X/ convert a byte of BCD (in r1) two to ascii digits and place those
X/ at (r3)+ and (r3)+ respectively.
X
Xbcd2msg:
X	clr	r0
X	div	$16.,r0
X	add	$'0,r0
X	add	$'0,r1
X	movb	r0,(r3)+
X	movb	r1,(r3)+
X	rts	pc
X
X/ convert a binary number (in r1) to a byte containing two bcd digits.
X/ return result in r0.
X
Xtobcd:
X	clr	r0
X	div	$100.,r0		/ truncate to max of 99
X	clr	r0
X	div	$10.,r0
X	ash	$4,r0
X	bis	r1,r0
X	rts	pc
X
X/ To calculate the day of the week (Sunday = 1) an algorithm found in
X/ "The Ready Reference (r) Weekly Planner 1986 ((c) 1986 Ready Reference)"
X/ is used.
X
Xt2dow:
X	jsr	r5,csv			/ save registers
X	movb	year,r4			/ low two digits (93) of year
X	mov	r4,r0
X	asr	r0			/ divide by 4
X	asr	r0			/  ignoring any remainder
X	add	r0,r4			/ add to year
X	movb	day,r0
X	add	r0,r4			/ add day of month
X	movb	month,r0
X	movb	m_magic-1(r0),r3
X	cmp	r0,$2			/ February Or January?
X	bgt	1f			/ no - br
X	movb	year,r0
X	add	$1900.,r0
X	mov	r0,-(sp)
X	jsr	pc,isleap		/ are we in a leap year?
X	mov	r0,(sp)+
X	beq	1f			/ no - br
X	dec	r3			/ adjust number from magic month table
X1:
X	add	r3,r4			/ add magic number to total
X	cmpb	year,$69.		/ before 1969?
X	bgt	2f			/ no - br, we're in the 20th century
X	add	$6,r4			/ adjustment for 21st century
X2:
X	mov	r4,r1
X	clr	r0
X	div	$7,r0			/ divide total by 7
X	dec	r1			/ Sunday?
X	bne	3f			/ no - br
X	mov	$7,r1			/ yes - set it to day 7
X3:
X	mov	r1,r0			/ put return value in right place
X	jmp	cret
X
X/ (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
X
Xisleap:
X	clr	r0
X	mov	2(sp),r1		/ get year
X	bit	$3,r1			/ easy check for "mod 4"
X	bne	1f			/ can't be a leap - br
X	div	$100.,r0		/ % 100
X	tst	r1			/ check remainder
X	bne	2f
X	bit	$3,r0			/ % 400
X	beq	2f
X1:
X	clr	r0
X	rts	pc
X2:
X	mov	$1,r0
X	rts	pc
X
Xinitoy:
X	tst	*$TOYCSR		/ strobe the clock register
X	clr	-(sp)			/ save previous high byte of register
X	movb	*$TOYCSR+1,(sp)		/ only bit 8 belongs to TOY!
X	bic	$1,(sp)			/ make sure bit 8 (TOY bit) is clear
X	mov	$2,r2			/ number of double words to send clock
X1:
X	mov	$35305,r0		/ first word of recognition code
X	jsr	pc,toyload		/ send it to clock
X	mov	$56243,r0		/ second word
X	jsr	pc,toyload		/ send it
X	sob	r2,1b			/ do the whole thing twice
X	tst	(sp)+			/ clean stack
X	rts	pc
X
X/ send contents of r0 to the TOY.  2(sp) has the old bits 9-15, bit 8
X/ has been cleared.
X
Xtoyload:
X	mov	$16.,r1			/ number of bits to send
X1:
X	mov	r0,r3			/ scratch copy
X	bic	$177776,r3		/ clear all but bit being sent
X	bis	2(sp),r3		/ merge in old_csr_bits
X	movb	r3,*$TOYCSR+1		/ send bit to clock
X	asr	r0			/ shift pattern down
X	sob	r1,1b			/ do all 16 bits in the word
X	rts	pc
X
X	.globl	_gettoy
X_gettoy:				/ (void)gettoy(&char[8]);
X	jsr	r5,csv			/ C callable
X	mov	4(r5),r2		/ buffer address
X	mov	$4,r3			/ number of words in buffer
X1:
X	mov	$16.,r4			/ number of bits in word
X2:
X	movb	*$TOYCSR+1,r0		/ low bit of top byte is a clock bit
X	asr	r0			/ put it in carry
X	ror	r1			/ ripple in at top end of r1
X	sob	r4,2b			/ do all 16 bits
X	mov	r1,(r2)+		/ store the word in the buffer
X	sob	r3,1b			/ do all 4 words
X	jmp	cret			/ and return
X
X	.data
XMtab:
X	.byte	31.,29.,31.,30.,30.,30.
X	.byte	31.,31.,30.,31.,30.,31.
Xm_magic:
X	.byte	1,4,4,0,2,5,0,3,6,1,4,6
Xerrmsg1:
X	<Cputype is not 93 or 94.  No TOY present\n\0>
Xtimmsg:
X	<Current TOY: >
Xtimbuf:
X	.=.+13.				/ room for YYMMDDhhmm.ss
X	<\n>
X	<Toyset\> \0>
X_module:
X	<Toyset\0>
X	.even
X	.bss
Xseconds:
X	.=.+1
Xminutes:
X	.=.+1
Xhours:
X	.=.+1
Xday:
X	.=.+1
Xmonth:
X	.=.+1
Xyear:
X	.=.+1
Xdow:
X	.=.+1
X	.even
Xbcd:
X	.=.+8.
Xline:
X	.=.+64.
SHAR_EOF
fi
exit 0
#	End of shell archive
