		page 62
;******************************************************************************
;*                                                                            *
;*                                                                            *
;*                AAAMM         MM PPPPPPPPPPPPP RRRRRRRRRRRR OOOOOOOOOOOOOO  *
;*              AAAAAMMM       MMM PPPPPPPPPPPPP RRRRRRRRRRRR OOOOOOOOOOOOOO  *
;*            AAA AAA MMM     MMM  PPP       PPP          RRR OOO        OOO  *
;*          AAA   AAA  MMM   MMM   PPP PPPPPPPPP RRRRRRRRRRRR OOO        OOO  *
;*        AAA     AAA   MMM MMM    PPP PPPPPPPPP RRRRRRRRRRRR OOO        OOO  *
;*      AAA       AAA     MMM      PPP                  RRR   OOO        OOO  *
;*    AAA  AAAAAAAAAA      M       PPP                   RRR  OOOOOOOOOOOOOO  *
;*  AAA  AAAAAAAAAAAA              PPP                    RRR OOOOOOOOOOOOOO  *
;*                                                                            *
;*                                                                            *
;*                                                                            *
;*                          AMPRO COMPUTERS INCORPORATED                      *
;*                              67 East Evelyn Avenue                         *
;*                             Mountain View, CA 94041                        *
;*                                 (415)962-0230                              *
;*                                                                            *
;*                                                                            *
;*                    SMARTCLK.Z80 for the LITTLE BOARD/PLUS                  *
;*                   Copyright (C) 1986 AMPRO Computers, Inc.                 *
;*                             ALL RIGHTS RESERVED.                           *
;*                                                                            *
;*           This code was developed for use with AMPRO hardware only.        *
;*        All other use is prohibited without prior written consent from      *
;*                            AMPRO Computers, Inc.                           *
;*                                                                            *
;******************************************************************************
;
;  version	date	by	description
;  -------    -------  ----     -----------------------------------------------
;    1.0      2 nov 86  fsw	Released version
;
; -----------------------------------------------------------------------------
; NOTE:	The Dallas SmartWatch will only work with the AMPRO LITTLE BOARD/PLUS
;	model 1B-1 or 1B-2.
;
;	The following modifications must be made to operate. On the back side
;	of the board cut the trace going to pin 20 on the 28 pin EPROM socket
;	labled U13.  Solder a jumper between the eprom socked U13 pin 20 and
;	pin 21 of the microprocessor labled U4. The SmartWatch will now
;	function.
;
;	May be assembled with a Z-80 assembler such as ZAS by MYTEK available
;	from 
;			Echelon, Inc.
;			885 N. San Antonio Road
;			Los Altos, CA. 94022
;			(415)948-3820
;	
; -----------------------------------------------------------------------------

control		equ	0		; prom control port
read		equ	4		; smartwatch read
wrt1		equ	3		; smartwatch write a 1
wrt0		equ	2		; smartwatch write a 0
off		equ	8000h-tpa	; offset for smartwatch interface
cr		equ	0dh		; carriage return
lf		equ	0ah		; line feed
tpa		equ	100h		; cpm tpa

; ------------------------------------------------------------------------
; begining of code

		org  	tpa
cmdline:	equ	$		; all the code between this point
					; and the label START: is moved above
					; eprom memory space. it is then
					; reused for command line and stack.
		jp	start

; ************************************************************************
; this code interfaces with the smartwatch and must be moved above the
; eprom memory space (0 to 8000h) to operate. the interface code is 
; located between the lines of '*'
;
; NOTE: the wakeup routine turns on the eprom socket and the readclk
;       and setclk routines turn it off.
;
; new time to write to smartwatch is in clkbuff

setclk:		call	wakeup+off	; init smartwatch
		ld	hl,clkbuff+off
		ld	b,8		; number of bytes to write

putbyte:	ld	d,b		; save byte count
		ld	b,8		; nunber of bits to write
		ld	a,(hl)		; get byte to write
putbit:		rra
		ld	c,a		; save
		jr	c,putone
		ld	a,(wrt0)	; write a zero
		jr	pb1

putone:		ld	a,(wrt1)	; writa a one
pb1:		ld	a,c		; get byte back
		djnz	putbit
		inc	hl		; next byte
		ld	b,d		; byte count
		djnz	putbyte
		ld	a,41h		; turn off eprom
		out	(control),a
		ret

; read the smartwatch registers, return with carry set if not sucessful

readclk:	call	wakeup+off	; init smartwatch
		ld	hl,clkbuff+off	; save area for read
		ld	b,8		; number of bytes to read

getbyte:	ld	d,b		; save byte count
		ld	b,8		; set bit count to 8
		xor	a,a		; clear a
		ld	c,a		; clear c

getbit:		ld	a,(read)
		and	a,1		; mask lsb
		or	a,c		; or in bits
		rrc	a		; move over for next bit
		ld	c,a		; and save results
		djnz	getbit

		ld	(hl),a		; put in buffer
		inc	hl		; next byte location
		ld	b,d		; restore byte count
		djnz	getbyte

		ld	a,41h		; 
		out	(control),a	; turn off eprom
		ld	a,(sw$reg4)+off
		and	a,a		; set flags
		jr	z,rderr		; register should be non-zero
		inc	a		; see if 0ffh
		jr	z,rderr
		ret

rderr:		scf
		ret

; initialize the smartclock
; NOTE: there is no stack when the prom socket is turned on.

wakeup:		pop	ix		; put ret address in 'ix'
		xor	a,a
		out	(control),a	; prom socket is now on
		ld	hl,wakepat+off
		ld	b,8		; byte count 
		ld	a,(read)	; reset clock

wakebyte:	ld	d,b		; save byte count in 'd'
		ld	b,8		; bit count
		ld	a,(hl)		; get first char

wakebit:	rra
		ld	c,a		; save
		jr	c,wrtone
		ld	a,(wrt0)	; write zero
		jr	wake1

wrtone:		ld	a,(wrt1)	; write one
wake1:		ld	a,c		; restore pattern
		djnz	wakebit		; bit count

		ld	b,d		; get byte count back
		inc	hl		; next pattern location
		djnz	wakebyte
		jp	(ix)		; return

wakepat:	db	0c5h,03ah,0a3h,05ch,0c5h,03ah,0a3h,05ch

clkbuff:				; 8 byte save area for smartclk reg
sw$reg0:	db	0		; tenths, hundredths seconds (00-99)
sw$reg1:	db	0		; seconds (00-59)
sw$reg2:	db	0		; minutes (00-59)
sw$reg3:	db	0		; hour    (00-23)
sw$reg4:	db	0		; day     (01-07)
sw$reg5:	db	0		; date    (01-31)
sw$reg6:	db	0		; month   (01-12)
sw$reg7:	db	0		; year    (00-99)

movlen:		equ	$-tpa

; ************** End of SmartWatch interface code ************************

stack:		equ	$		; use this area for stack 
oldstack	ds	2

; initialize memory space

start:		ld	hl,tpa		; source
		ld	de,8000h	; destination
		ld	bc,movlen	; bytes to move
		ldir			; move program above rom space
		xor	a,a		; clear a
		ld	hl,tpa		; clear cmdline and stack area
		ld	b,start-tpa
clearcmd:	ld	(hl),a
		inc	hl
		djnz	clearcmd

begin:		ld	(oldstack),sp	; save stack pointer
		ld	sp,stack
		jp	cont		; jump past help

helpmsg:	db	'SMARTCLK Ver 1.0'
		db	cr,lf
		db	'Copyright (c) 1986, AMPRO Computers, Inc.'
		db	cr,lf,lf
		db	'Usage: SMARTCLK    - display date and time'
		db	cr,lf
		db	'       SMARTCLK /S - set SmartWatch'
		db	cr,lf
		db	'       SMARTCLK /T - test SmartWatch'
		db	cr,lf
		db	'$'

cont:		call	readclk+off	; read the smartwatch
		jp	c,swerror	; error reading smartwatch
		ld	hl,80h		; point to command line buffer
		ld	a,(hl)		; see if entry
		or	a,a
		jp	z,show$dt	; no, show date and time
space:		inc	hl
		ld	a,(hl)
		cp	' '		; filter leading spaces
		jr	z,space

none:		ex	de,hl		; 'de' to command line
		call	compare
		db	'/T',0		; test
		jp	nc,test
		call	compare
		db	'/S',0		; set time
		jp	nc,settime

		ld	de,helpmsg	; give help
		jr	comm

swerror:	ld	de,errmsg
		jr	comm

dateerr:	ld	de,dterrmsg
		jr	comm

timeerr:	ld	de,tmerrmsg
comm:		call	print$string
exit:		ld	sp,(oldstack)	; and exit
		ret

; show the date and time

show$dt:	call	shw$dt		; display date and time
		jr	exit

; test the smartwatch by reading until interrupted by console input

test:		call	readclk+off
		jr	c,swerror
		call	showtime
		ld	c,11		; get console status
		call	5
		or	a,a
		jr	nz,testend	; if any char typed
		ld	a,cr
		call	print$char
		jr	test

testend:	ld	c,1		; con input
		call	5		; get the character
		jr	exit		; and throw away

; set the date and time

settime:	call	print
		db	cr,lf,'Enter day of week (Mon-Sun) ',0
		call	rdbuff		; get input
		ld	de,cmdline+1	; see if input
		ld	a,(de)
		or	a,a
		jp	z,exit
		inc	de
		call	compare
		db	'SUN',0
		ld	a,1
		jp	nc,stm1
		call	compare
		db	'MON',0
		ld	a,2
		jp	nc,stm1
		call	compare
		db	'TUE',0
		ld	a,3
		jp	nc,stm1
		call	compare
		db	'WED',0
		ld	a,4
		jp	nc,stm1
		call	compare
		db	'THU',0
		ld	a,5
		jp	nc,stm1
		call	compare
		db	'FRI',0
		ld	a,6
		jp	nc,stm1
		call	compare
		db	'SAT',0
		ld	a,7
		jp	nc,stm1
error:		call	print
		db	cr,lf,'Invalid entry',0
		jp	exit

stm1:		or	a,10h		; set reset bit
		ld	(sw$reg4)+off,a	; day of week to smartwatch reg
		call	print
		db	cr,lf,'Enter date (mm-dd-yyyy) ',0
		call	rdbuff
		ld	hl,cmdline+1
		ld	a,(hl)
		or	a,a
		jp	z,error
		call	valid		; test for valid numeric entry
		call	strng2bcd	; convert cmdline to bcd
		or	a,a		; valid month (01-12)
		jp	z,dateerr
		cp	12h+1
		jp	nc,dateerr
		ld	(sw$reg6)+off,a	; put month

		inc	hl
		push	de		; save binary month
		call	strng2bcd	; get date
		pop	de		; get binary month back
		push	hl		; save pointer
		dec	de		; make month 0 to 11
		ld	hl,mnths	; offset to number of days in month
		add	hl,de
		cp	(hl)		; see if more than max days in month
		pop 	hl		; 
		jp	nc,dateerr
		ld	(sw$reg5)+off,a	;

		inc	hl
		call	strng2bcd	; get year
		ld	hl,1979		; make sure 4 digit century entered
		sbc	hl,de		; 'de'=binary century
		jp	p,dateerr
		cp	99h+1		; more than 99
		jp	nc,dateerr
		ld	(sw$reg7)+off,a	; store year

		call	print
		db	cr,lf,'Enter time (hh:mm:ss) ',0
		call	rdbuff		; get input
		ld	hl,cmdline+1
		ld	a,(hl)		; get number of characters typed
		or	a,a
		jp	z,error		; no input
		call	valid		; test for valid numeric asci

		call	strng2bcd	; get hour
		cp	24h+1
		jp	nc,timeerr	;
		ld	(sw$reg3)+off,a	; store hour

		inc	hl
		call	strng2bcd	; get minute
		cp	59h+1
		jp	nc,timeerr
		ld	(sw$reg2)+off,a	; store minute

		inc	hl
		ld	a,(hl)
		or	a,a		; see if seconds entered
		jr	z,stm4
		call	strng2bcd	; get seconds
		cp	59h+1
		jp	nc,timeerr
stm4:		ld	(sw$reg1)+off,a

		xor	a,a
		ld	(sw$reg0)+off,a	; clear hundreths of seconds
		call	setclk+off	; set the time
		call	readclk+off	; show the date and time
		call	shw$dt
		jp	exit

; show date and time

shw$dt:		ld	de,curmsg
		call	print$string
		ld	a,(sw$reg4)+off	; get day of week
		and	a,07h		; mask off extra 
		call	bcd2bin		; 
		dec	a		; make 0 to 6
		sla	a		; x2
		ld	e,a
		ld	d,0
		ld	hl,dayofweek
		add	hl,de
		ld	e,(hl)
		inc	hl
		ld	d,(hl)		; print day of week
		call	print$string

		ld	a,(sw$reg6)+off	; get month
		and	a,1fh		; mask month (01 to 12)
		call	bcd2bin
		dec	a		; make 0 to 11
		sla	a		; x2
		ld	e,a
		ld	d,0
		ld	hl,month
		add	hl,de
		ld	e,(hl)
		inc	hl
		ld	d,(hl)		; month string in 'de'
		call	print$string

		ld	a,(sw$reg5)+off	; get date
		and	a,3fh		; mask (01 to 31)
		ld	hl,date
		call	a2asc
		ld	de,date
		call	print$string

		ld	a,(sw$reg7)+off	; get year
		cp	20h		; less than 20
		jr	c, twnty
		ld	hl,cen21a
		sub	20h		; sub 21st century offset
		call	a2asc
		ld	de,cen21
		jr	prn$yr

twnty:		add	80h		; add offset for 1980
		ld	hl,cen20a
		call	a2asc
		ld	de,cen20
prn$yr:		call	print$string

show$time$msg:	ld	de,timemsg
		call	print$string

showtime:	ld	a,(sw$reg3)+off	; get hour
		ld	hl,hrmsg
		call	a2asc

		ld	a,(sw$reg2)+off	; get minute
		ld	hl,minmsg
		call	a2asc

		ld	a,(sw$reg1)+off	; get second
		ld	hl,secmsg
		call	a2asc

		ld	a,(sw$reg0)+off	; get hundredth of second
		ld	hl,hunmsg
		call	a2asc

		ld	de,hrmsg	; display the time
		call	print$string
		ret

; convert decimal character string pointed to by 'hl'.
; returns: 'a' equal to bcd value between 0 and 99.
;          'de' equals the binary value of string
;          'hl' points to delimiter
;
strng2bcd:	push	bc		; save
		ld	de,0
stng1:		ld	a,(hl)		; get char
		cp	'0'
		jr	c,stngext	; less than ascii '0'
		cp	'9'+1
		jr	nc,stngext	; more than ascii '9'
		sub	'0'
stng10:		push	hl		; save pointer
		ld	h,d		; multiply de x10
		ld	l,e
		add	hl,hl		; *2
		add	hl,hl		; *4
		add	hl,de		; *5
		add	hl,hl		; *10
		ld	e,a
		ld	d,0
		add	hl,de
		ex	de,hl
		pop	hl
		inc	hl
		jr	stng1

; convert the binary value in 'de' to bcd value between 0 and 99 in 'a'.

stngext:	push	de		; save bcd value
		push	hl		; save pointer
		xor	a,a		; clear flags
		ex	de,hl
		ld	de,1980
		sbc	hl,de		; sub 1980
		jp	p,cvt		; ok if positive
		ccf			; was set if here
		adc	hl,de		; add it back
cvt:		ld	a,l		; 'a'= 0h to 63h
		ld	c,0ffh		;
lp10:		inc	c
		sub	10		; -10 loop
		jr	nc,lp10
		add	10		; make back positive
		ld	b,a		; save 1's
		ld	a,c		; get 10's
		sla	a
		sla	a
		sla	a
		sla	a		; 10's to upper nibble
		or	a,b		; or in 1's
		pop	hl
		pop	de
		pop	bc
		ret

; convert bcd number in 'a' to ascii and store number to 'hl' and 'hl'+1

a2asc:		push	af
		and	a,0f0h		; do upper nibble
		rra
		rra
		rra
		rra
		or	a,'0'
		ld	(hl),a
		inc	hl
		pop	af		; do lower nibble
		and	a,0fh
		or	a,'0'
		ld	(hl),a
		ret

; print the char in 'a'

print$char:	push	bc
		push	de
		push	hl
		ld	e,a
		ld	c,2		; print character
		call	5		; cp/m
		pop	hl
		pop	de
		pop	bc
		ret

; cpm function 9h, print string terminated with '$'. 
; "de" points to string address

print$string:	push	af
		push	bc
		push	hl
		ld	c,9
		call	5		; cpm entry
		pop	hl
		pop	bc
		pop	af
		ret

; get command line input

rdbuff:		ld	hl,cmdline	; point to command line
		push	hl
		ld	a,40
		ld	(hl),a		; 40 character command line
		ld	b,a		; set up count for clear buffer
		xor	a		; clear buffer
clrbuff:	inc	hl
		ld	(hl),a
		djnz	clrbuff
		pop	de		; get command line ptr back to 'de'
		ld	c,10
		call	5
		ret

; print a ascii string terminated with '0' contained in the code

print:		pop	hl		; string address is on stack
		ld	a,(hl)
		or	a,a		; see if end
		jr	z,prnend
		call	print$char	; print the character
		inc	hl
		jr	print+1

prnend		jp	(hl)

; scan the command line for valid entry
; called with 'hl' pointing to command line count

valid:		ld	a,(hl)		; get number of characters typed
		inc	hl
		dec	a		; make sure more than one char
		jr	z,notvalid+1
		inc	a		; restore value
		cp	' '
		jr	z,valid		; filter spaces
		push	hl		; save pointer to first char

valid1:		ld	a,(hl)
		inc	hl
		and	a,a
		jr	z,validext
		cp	'-'
		jr	z,valid1
		cp	':'
		jr	z,valid1
		cp	'0'
		jr	c,notvalid
		cp	'9'+1
		jr	nc,notvalid
		jr	valid1

validext:	pop	hl
		ret

notvalid:	pop	hl
		jp	error

; compare a string of characters addressed by 'de' to string
; pointed to by stack address.

compare:	ex	(sp),hl		; 
		push	de		; save source address
cmpa:		ld	a,(hl)

		and	a,a		; zero is end
		jr	z,same		; yes, string was equal
		ld	a,(de)		; get string char
		cp	'Z'+1		; less than 'Z'
		jr	c,cmpb
		and	5fh		; make upper case
cmpb:		cp	(hl)		; compare
		jr	nz,notsame	; not the same
		inc	hl		; next characters
		inc	de
		jr	cmpa		; till through or not same

notsame:	xor	a,a		; keep going till end of string
		inc	hl
		cp	(hl)
		jr	nz,notsame+1
		scf			; show error

same:		pop	de		; restore source address
		ex	(sp),hl		; 'hl' has return address
		ret

; BCD to Binary
; 'A' = bcd number to convert, returns biniary number in 'A'
;
bcd2bin:	push	bc
		ld	c,a		; save for a moment
		and	0fh		; make low nibble
		ld	b,a		; save it
		ld	a,c		; get bcd back
		and	0f0h		; upper nibble
		rrca			; to low nibble
		rrca
		rrca
		rrca
		add	a,a		; x 2
		ld	c,a		; save results
		add	a,a		; x 4
		add	a,a		; x 8
		add	a,c		; x10
		add	a,b		; have biniary equivalent in 'a'
		pop	bc
		ret

curmsg:		db	cr,lf,'Current date is $'
date:		db	'  ,$'
cen20:		db	'19'
cen20a:		db	'  $'
cen21:		db	'20'
cen21a:		db	'  $'
timemsg:	db	' - Time $'
hrmsg:		db	'  :'
minmsg:		db	'  :'
secmsg:		db	'  .'
hunmsg:		db	'  $'

dterrmsg:	db	cr,lf,'Invalid date$'
tmerrmsg:	db	cr,lf,'Invalid time$'
errmsg:		db	cr,lf,'error reading SmartWatch$'

dayofweek:	dw	day1
		dw	day2
		dw	day3
		dw	day4
		dw	day5
		dw	day6
		dw	day7

month:		dw	mon1
		dw	mon2
		dw	mon3
		dw	mon4
		dw	mon5
		dw	mon6
		dw	mon7
		dw	mon8
		dw	mon9
		dw	mon10
		dw	mon11
		dw	mon12

mon1:		db	'January $'
mon2:		db	'February $'
mon3:		db	'March $'
mon4:		db	'April $'
mon5:		db	'May $'
mon6:		db	'June $'
mon7:		db	'July $'
mon8:		db	'August $'
mon9:		db	'September $'
mon10:		db	'October $'
mon11:		db	'November $'
mon12:		db	'December $'

day1:		db	'Sunday, $'
day2:		db	'Monday, $'
day3:		db	'Tuesday, $'
day4:		db	'Wednesday, $'
day5:		db	'Thursday, $'
day6:		db	'Friday, $'
day7:		db	'Saturday, $'

mnths:		db	31h+1,29h+1,31h+1,30h+1,31h+1,30h+1
		db	31h+1,31h+1,30h+1,31h+1,30h+1,31h+1

		end


