;') pSetClock 0.2 © 1992 PdT
;') Based on SetTime 0.9 by Acki from Fish
;') A replacement for SetClock
;') This code is not quite in the public domain; you may do anything except
;') stop others from distributing this code or to claim it as your own.
;') 
;') Use at your own risk; all I certify is that it is not, in whole or
;') part, designed to stuff up your system.
 
; -- Exec library offsets.

AllocMem	= -198
FreeMem		= -210
FindTask	= -294
AllocSignal	= -330
FreeSignal	= -336
OpenDevice	= -444
CloseDevice	= -450
DoIO		= -456

rsave:	macro
	movem.l	\1,-(SP)
	endm

rrest:	macro
	movem.l	(SP)+,\1
	endm

Call:	macro
	jsr	\1(a6)
	endm

clra:	macro
	suba.l	\1,\1
	endm

	INCLUDE "exec/types.i"
	INCLUDE "devices/timer.i"

tio_secs	=	IOTV_TIME+TV_SECS
tio_cmd		=	IO_COMMAND
tio_mport	=	MN_REPLYPORT
tio_type	=	LN_TYPE
tio_dev		=	IO_DEVICE

MASK	EQUR	d7
SVMASK	EQUR	d6
TEMP	EQUR	d5
dSecs	EQUR	d4
NUM	EQUR	d3
YY	EQUR	d2
MM	EQUR	d1
DD	EQUR	d0

Clock	EQUR	a0
Table	EQUR	a1
tIOreq	EQUR	a2
tMport	EQUR	a3
RETNVAL	EQUR	a4

MSGMEM	equ	$10001

A:	rsave	d1-d7/a0-a6
	move.l	4,a6
	move.l	#20,RETNVAL

	moveq	#0,d1
strwhi	move.b	(a0)+,d1		; strip whitespace
	cmp.b	#' ',d1
	dbne	d0,strwhi
	beq	ZZ
	and.b	#255-32,d1		; case fold and
	moveq	#2,d0			; fetch 4 characters.
fetchr	asl.l	#8,d1
	move.b	(a0)+,d1
	and.b	#255-32,d1
	dbra	d0,fetchr
	move.l	d1,NUM

	move.l	#MP_SIZE,d0		; Create a message port
	move.l	#MSGMEM,d1
	Call	AllocMem
	move.l	d0,tMport

	move.b	#NT_MSGPORT,LN_TYPE(tMport)	; init type field.
	lea	MP_MSGLIST(tMport),a0	; init message list.
	NEWLIST a0

	clra	a1			; get and save address for signal
	Call	FindTask
	move.l	d0,MP_SIGTASK(tMport)
	
	moveq	#-1,d0			; allocate signal.
	Call	AllocSignal
	move.b	d0,MP_SIGBIT(tMport)

	move.l	#IOTV_SIZE,d0		; Allocate & init a (public) IOReq.
	move.l	#MSGMEM,d1		; Public & cleared memory
	Call	AllocMem
	move.l	d0,tIOreq

	move.b	#NT_MESSAGE,tio_type(tIOreq)
	move.l	#IOTV_SIZE,d0
	move.l	d0,MN_SIZE(tIOreq)
	move.l	tMport,MN_REPLYPORT(tIOreq)

	lea	timnam(PC),a0		; Open timer.device
	moveq	#0,d0
	move.l	tIOreq,a1
	move.l	d0,d1
	Call	OpenDevice

	move.l	NUM,d1
	move.l	#0,RETNVAL
	cmp.l	#"LOAD",d1
	beq	doload
	cmp.l	#"SAVE",d1
	beq	dosave
	move.l	#20,RETNVAL
cleanup:
	move.l	tIOreq,a1		; close timer.device
	Call	CloseDevice
	moveq	#0,d0			; free signal bit
	move.b	MP_SIGBIT(tMport),d0
	Call	FreeSignal
	move.l	tMport,a1		; free message port memory
	move.l	#MP_SIZE,d0
	Call	FreeMem
	move.l	tIOreq,a1		; free IOReq memory
	move.l	#IOTV_SIZE,d0
	Call	FreeMem
ZZ:	move.l	RETNVAL,d0
	rrest	d1-d7/a0-a6
	rts

LEAPMASK	= $22222022
DAYSECS		= 24*60*60
YEARDAYS	= 365

fetch:	macro
	move.l	-(Clock),TEMP
	and.b	#%1111,TEMP
	move.b	TEMP,NUM
	endm

Getdig:
	fetch
	lsl.b	#1,NUM		; DD += NUM*10
	add.l	NUM,DD
	lsl.b	#2,NUM
	add.l	NUM,DD
	fetch
	add.l	NUM,DD		; DD += NUM
	rts

addon:	macro
	bsr	Getdig
	endm

mul60:	macro
	lsl.l	#2,DD
	move.l	DD,TEMP
	lsl.l	#4,DD
	sub.l	TEMP,DD
	endm

doload:
	lea	($dc0000+12*4),Clock
	moveq	#-78,YY		; Pre-adjust for choice of epoch.
	moveq	#0,NUM
	move.l	-(Clock),TEMP
	and.b	#%1111,TEMP
	move.b	TEMP,NUM	; (number of decades - 190) in NUM.
	move.l	NUM,TEMP	; turn decades into # of years
	mulu	#10,TEMP
	add.l	TEMP,YY		; and add to year count.
	moveq	#0,NUM
	fetch			; Fetch years.
	add.w	NUM,YY		; now we have # of yrs since 78 in YY
	move.l	#LEAPMASK,MASK	; convert yrs to # of days (in MM)
	move.l	MASK,SVMASK
	asl.l	YY,SVMASK	; preshift saved mask
	move.l	#YEARDAYS,NUM
	moveq	#0,MM
y2d:	asl.l	#1,MASK		; add #days in each year to sum in MM
	addx.w	NUM,MM
	dbra	YY,y2d
	lea	xntab(PC),Table	; pick month table on leap/nonleap
	asl.l	#1,SVMASK
	bcc	nonleap
	lea	xltab(PC),Table
nonleap	subx.w	NUM,MM		; and compensate for last add in y2d.
	moveq	#0,NUM
	moveq	#0,DD
	addon			; fetch months
	exg	DD,MM		; swap counter and accumulator
m2d:	move.b	(Table)+,NUM
	add.w	NUM,DD
	dbra	MM,m2d
	addon			; fetch days
	subq.w	#1,DD		; compensate for +1 for dates.
	mulu	#(DAYSECS/2),DD	; multiply by seconds per day
	add.l	DD,DD
	move.l	DD,dSecs	; and store in dSecs
	moveq	#0,NUM
	moveq	#0,DD
	addon			; hours
	mul60
	addon			; minutes
	mul60
	addon			; seconds
	add.l	DD,dSecs
	move.l	tIOreq,a1
	move.w	#TR_SETSYSTIME,tio_cmd(a1)
	move.l	dSecs,tio_secs(a1)
	Call	DoIO
	bra	cleanup

;-------------------------------------------------------------------------

PutXX:	macro
	divu	#10,NUM		; derive and save high and low digits.
	move.b	NUM,(\1-1)*4+HotSpot(Clock)
	swap	NUM
	move.b	NUM,(\1-2)*4+HotSpot(Clock)
	endm

HotSpot	equ	1

PutSec:	macro
	divu	#(\1),dSecs
	move.b	dSecs,((\2)-1)*4+HotSpot(Clock)
	clr.w	dSecs
	swap	dSecs
	endm

dosave:	;-- Save system time to battery backed TOD clock.
	move.l	tIOreq,a1		; fetch system time.
	move.w	#TR_GETSYSTIME,tio_cmd(a1)
	Call	DoIO			;!! Assumes IO always succeeds.
	move.l	tIOreq,a1
	move.l	tio_secs(a1),dSecs	; seconds since 1st Jan 1978.
	moveq	#0,DD			; split into days and seconds
	moveq	#0,NUM
	move.w	#(DAYSECS/2),NUM	; 2**17 > DAYSECS > 2**16
	divu	NUM,dSecs
	move.w	dSecs,DD		; Save half days in DD
	clr.w	dSecs
	swap	dSecs
	lsr.w	#1,DD			; DD is days since 1st Jan 1978.
	bcc	isAM
	add.l	NUM,dSecs		; add a half-day woth of seconds
isAM:	moveq	#0,YY
	move.l	#LEAPMASK,MASK
	move.l	MASK,SVMASK
	move.w	#YEARDAYS,NUM		; top word known clear
d2yl:	addq.l	#1,YY			; pre-increment year
	asl.l	#1,MASK			; get X to adjust #days on leaps
	subx.l	NUM,DD			; and sub #days in this year.
	bpl	d2yl
	add.l	NUM,DD			; partially compensate for overshoot
	moveq	#0,TEMP
	moveq	#0,MM
	lea	ntab(PC),Table
	rol.l	YY,SVMASK		; set carry to leap/nonleap
	bcc	d2ml
	lea	ltab(PC),Table		; select leap year table
	addq.l	#1,DD			; adjust days since leap year longer
d2ml:	addq.w	#1,MM			; pre-increment month
	move.b	-1(Table,MM.w),TEMP	; move byte days in month into long
	sub.w	TEMP,DD
	bpl	d2ml
	addx.w	TEMP,DD			; correct for last subtraction +
	moveq	#0,TEMP
	moveq	#77,NUM			; adjust years properly
	lea	($dc0000),Clock
	add.l	YY,NUM
	PutXX	12			; save years
	move.l	MM,NUM
	PutXX	10			; save months
	move.l	DD,NUM
	PutXX	8			; save days
	PutSec	(10*DAYSECS/(24)),6	; 10s of hours
	PutSec	(DAYSECS/(24)),5	; 1s of hours
	PutSec	(10*DAYSECS/(24*60)),4	; 10s of minutes
	PutSec	(DAYSECS/(24*60)),3	; 1s of minutes
	PutSec	10,2			; 10s of seconds
	move.b	dSecs,HotSpot(Clock)	; seconds
	bra	cleanup

xntab:	dc.b	0,0
ntab:	dc.b	31,28,31,30,31,30,31,31,30,31,30,31
xltab:	dc.b	0,0
ltab:	dc.b	31,29,31,30,31,30,31,31,30,31,30,31
timnam:	dc.b	"timer.device",0

	END
