Page ,132
Title	Replacement system clock driver for clock boards using MM 58167 AN clock chip.


comment $
  This is an installable device driver to access the battery driven
clock on I/O port.
  Use with AST or TECMAR clock boards.	Specify which one you want in the
'MACROS' section.  If the choice is not specified you will have assembly error
messages.

  To use, assemble this cource file using MASM.EXE (Macro Assembler from
Microsoft) then link it using DOS LINK.EXE and finally convert it to a .BIN
file using DOS EXE2BIN.EXE.  Rename the final file CLOCK.SYS.  Use EDLIN.COM
to create a file named CONFIG.SYS with the statement 'DEVICE = CLOCK.SYS'.
It will then be loaded automatically at start-up time.
  Using the command file generated by this assembly code as a simple command
will hang up the computer.
  When the object code generated by the Macro assembler from this source code
is linked, a warning "No STACK segment" and a message "1 error detected" should
be ignored.

  Variable names and labels start with a capital letter followed
by italic characters.
  Macro assembler operators are in italics.
  8086 microprocessor registers are in capital letters.

  The MM 58167 AN clock chip has 8 clock counters and 8 data registers for
setting the alarm comparator.  It does not provides any facilities for
keeping track of the year or wether it is a leap year.	Hence the maximum
number of days in February is 28 (storing a 29 in the day of the month counter
while the month counter is 2 will set the corresponding clock counters to
March 1st).  The maximum number of days in the other months are 30 or 31
depending on the month.
  To provide for these deficiencies, the year, a copy of the month and
a leap year flag are stored in the data registers.  Their use is explained
in the subsequent program.

  Dr The Hung Luv, DMD.  Montreal, Mar 84.
  Reviewed and modified by Pierre Caouette. <71426,1561>
  References:
- Installable device drivers. p. 14-1 to 14-34.  (In Disk Operating System.
Ver. 2, (Boca Raton), Microsoft, 1983.	XXII + discontinuous pagings.)
- Broadwell, K. David.	Real-time clocks and PC-DOS 2.0.  Byte magazine,
9(1):442-450, Jan. 1984.
- Product specification : MM58167 AN clock chip. National Semiconductor.
End of comment $


;-----------------------------------------------------------------------;
;  Macros								;
;-----------------------------------------------------------------------;

Status	macro	State,Err,Rc
	ifidn	<State>,<Done>
		or	ES:word ptr Srh_sta_fld[BX],0100H
	endif
	ifidn	<State>,<Busy>
		or	ES:word ptr Srh_sta_fld[BX],0200H
	endif
	ifidn	<Err>,<Error>
		or	ES:word ptr Srh_sta_fld[BX],1000H
	endif
	ifnb	<Rc>
		or	ES:word ptr Srh_sta_fld[BX],Rc
	endif
	endm


;****************************************************************************
;  Write '<Tecmar>' after the statement 'ifnb' if you want the device driver
;installed for a Tecmar board.	Otherwise just write '<>'.
	ifnb	<>
;****************************************************************************
;	The following are macros to be used with a Tecmar clock board
Valid_code_mcr	macro
Valid_code	equ	0ABH	;Validate chip code.  Can be set to any
				;	arbitrary value.
		endm
Clk_base_port	macro
Clock_base_port equ	37DH	;Base port = clock board I/O address. Start
				;with clock latch of Tecmar Board's. This port
				;must be writen with the address of the data
				;to be read or writen at "037F" location.

				;To obtain the data I/O port address (this is
				;the location to read or write the data
				;specified at "037D") add 2 to the clock base
				;port.
		endm
Clock_access	macro
Tecmar_access	proc	near
;  Procedure to read/write clock IC. AH=0 is read, any other value in AH is
;write.
;  Data passed and returned in AL, DX=index register (clock latch).
		push	AX
		mov	AL,DL
		mov	DX,Clock_base_port
		out	DX,AL
		add	DX,2
		pop	AX
		or	AH,AH		;Read or Write? (Same as 'cmp AH,0')
		jnz	IO_write
		in	AL,DX
		jmp	Terminate
IO_write:	out	DX,AL
Terminate:	ret
Tecmar_access	endp
		endm
Read	macro
		mov	AH,0
		call	Tecmar_access
	endm
Write	macro
		mov	AH,1
		call	Tecmar_access
	endm
else

;****************************************************************************
;  Write '<AST>' after the statement 'ifnb' if you want the device driver
;installed for an AST board.  Otherwise just write '<>'.  If another choice
;has already been made before, then this option is not valid.
	ifnb	<AST>
;****************************************************************************
;	The following are macros to be used with an AST Tecmar clock board
Valid_code_mcr	macro
Valid_code	equ	0DEH	;Validate chip code.  Can be set to any
				;	arbitrary value.  0DEH is used by
				;	the AST support programs Ver 4.0
		endm
Clk_base_port	macro
Clock_base_port equ	2C0H	;Base port = clock-calendar chip first I/O
				;	address on AST board.
		endm
Clock_access	macro
Ast_access	proc	near
;  Procedure to read/write clock IC. AH=0 is read, any other value in AH is
;write.
;  Data passed and returned in AL, DX=index register (clock latch).
		add	DX,Clock_base_port
		or	AH,AH		;Read or Write? (Same as 'cmp AH,0')
		jnz	IO_write
		in	AL,DX
		jmp	Terminate
IO_write:	out	DX,AL
Terminate:	ret
Ast_access	endp
		endm
Read	macro
		mov	AH,0
		call	Ast_access
	endm
Write	macro
		mov	AH,1
		call	Ast_access
	endm

	endif
endif


;-----------------------------------------------------------------------;
;  Equates								;
;-----------------------------------------------------------------------;
Srh		equ	0	;Static request header start
Srh_len 	equ	13	;   "      "      "    length
Srh_len_fld	equ	Srh	;   "      "      "       "   field
Srh_ucd_fld	equ	Srh+1	;Srh unit code field
Srh_ccd_fld	equ	Srh+2	;Srh command code field
Srh_sta_fld	equ	Srh+3	;Srh Status field
Srh_res_fld	equ	Srh+5	;Srh reserved area field

;	Input/Output
Buf		equ	14	;Offset into the requested block of
				;	data (buffer) transfer address
Buf_len 	equ	4	;Buffer length

;	Initialization
Br_addr_0	equ	14	;Character device usually only sets ending
Br_addr_1	equ	Br_addr_0+2	;address and returns
Br_addr_len	equ	4

;	Offsets from clock base port
Ram_mo_ofst	equ	9H	;  "    of month checker in RAM
Ram_leap_ofst	equ	8H	;This clock register only store the
				;	upper nibble (4 bytes)
Ram_yr_ofst	equ	0AH	;  "    of year in RAM
Cntr_reset_ofst equ	12H	;  "    of reset (RAM reset is one up at 13H)
Status_bit_ofst equ	14H	;  "    to status byte. Unreliable read = 80,
				;	Ok = 00
Valid_ofst	equ	0BH	;  "    of validation code in RAM
;	Miscellaneous
Valid_code_mcr			;Defined in section 'macros'.
Leap_code	equ	10H	;Because only upper nibble is stored, 10H is
				;	would be retained
No_clock_cntr	equ	8H	;Number of clock counters on chip
No_clock_RAM	equ	2H	;Number of clock RAM stored in Clock_cntr
				;	Used in procedure Set_clock to
				;	calculate end address of Clock_cntr
Clk_base_port			;Defined in section 'macros'.


;-----------------------------------------------------------------------;
;  Structures								;
;-----------------------------------------------------------------------;
cseg	segment para public 'Code'

Clock_cntr_type struc
Ten_thous	db	?	;Counter - 1/10,000 of seconds	  BCD
Hunds		db	?	;Counter - 1/100 and 1/10 seconds BCD
Secs		db	?	;Counter - seconds		  BCD
Mins		db	?	;Counter - minutes		  BCD
Hrs		db	?	;Counter - hours		  BCD
Wek_day 	db	?	;Counter - days of the week	  BCD
Day		db	?	;Counter - days of the month	  BCD
Mo		db	?	;Counter - month		  BCD
Leap_adj	db	?	;RAM - leap year adjust flag
Yr		db	?	;RAM - year stored in RAM. Its offset is
				;	not continous with the counters.
				;	Care should be taken when writing it
				;	in RAM to use the Ram_yr_ofst.
				;AST clock support programs store it in HEX
				;	so we use the same format
Clock_cntr_type ends


Clk	proc	far
	assume	CS:cseg,ES:cseg,DS:cseg
Begin:
;-----------------------------------------------------------------------;
;  Special device header						;
;-----------------------------------------------------------------------;
Next_dev	dd	-1	;Pointer to next device header field.
				;	If no other device then Next_dev = -1
Attribute	dw	8008H	;Attribute of clock device (specification
				;	of type of device)
Strategy	dw	Dev_strategy	;Pointer to device strategy
Interrupt	dw	Dev_int 	;Pointer to interrupt handler
Dev_name	db	'CLOCK$'        ;Device name
		db	2 dup(?)	;Filler for an extra 2 bytes because
					;	"device name" has 8 bytes
Rh_off		dw	?	;Request header offset
Rh_seg		dw	?	;Request header segment
User_buf	dd	?	;User transfer address

;	Function table
Funtab		label	byte
		dw	Init	;Initialization -Used first time device is
				;		 called
		dw	Media_check	;(Block only)		- Not used
		dw	Build_bpb	;     "                 - Not used
		dw	Ioctl_in	;Ioctl input		- Not used
		dw	Input		;Input(read) = get the time/date
		dw	Nd_input	;Non-destructive input (char only)
					;			- Not used
		dw	In_stat 	;Char only		- Not used
		dw	In_flush	;    "                  - Not used
		dw	Output		;Output(write) = set time/date
		dw	Out_verify	;Output w/ verify	- Not used
		dw	Out_stat	;Char only		- Not used
		dw	Out_flush	;    "                  - Not used
		dw	Ioctl_out	;Ioctl ouput		- Not used

;	Local data block in DOS 2.0 format
Clk_tbl 	label	byte
		dw	?	;Days since 1-1-80 = 2 bytes
		db	?	;Minutes = 1 byte
		db	?	;Hours = 1 byte
		db	?	;1/100ths of seconds = 1 byte
		db	?	;Seconds = 1 byte


;	Table of current clock values
Clock_cntr	Clock_cntr_type <>	;Allocates according to predefined
					;	structure. Look under
					;	"Structures"


;-----------------------------------------------------------------------;
;  Local procedures							;
;-----------------------------------------------------------------------;

In_save 	proc	near	;Save caller's buffer address
		mov	AX,ES:word ptr Buf[BX]
		mov	CS:User_buf,AX
		mov	AX,ES:word ptr Buf+2[BX]
		mov	CS:User_buf+2,AX
		ret
In_save 	endp

Dec_hex 	proc	near
;  Converts small packed BCD to HEX.
;  Variable passed and returned in AL.
;  Registers BX, CL used.
		push	AX
		mov	CL,4
		shr	AL,CL
		pop	BX
		mov	BH,10
		mul	BH
		and	BL,0FH
		add	AL,BL
		ret
Dec_hex 	endp

Hex_dec 	proc	near
;  Converts HEX numbers up to 63H (99 dec) to BCD
;  Variable passed and returned in AL.
;  Registers BX, CL used.
		sub	AH,AH
		mov	BL,10
		div	BL
		mov	CL,04
		shl	AL,CL
		xchg	AL,AH
		or	AL,AH		;BCD in AL
		ret
Hex_dec 	endp

Daysconv	proc	near
;  Convert the date mm-dd-yy to its number of days since 1-1-80.
;  yy is passed in Clock_cntr.Yr
;  mm is passed in Clock_cntr.Mo
;  dd is passed in Clock_cntr.Day
;  number of days returned in first 2 bytes of Clock_tbl
;  AX, BX, CX, DX are used.  It is the responsibility of the caller to save
;them if they would be needed later.
;  CX = daysleft.
		mov	AX,365
		xor	BH,BH
		mov	BL,Clock_cntr.Yr	;BL = yy
		mul	BX			;DX + AX = 365 * yy
		mov	CX,AX			;CX = 365 * yy, ignoring
						;	DX. I.e. any value over
						;	65536. Calculations
						;	only valid for
						;	Clock_cntr.Yr < 179
		mov	AX,BX			;AL = yy
		or	AL,AL			;Same as 'cmp AL,0'
		jnz	Yr_not_nul
		dec	CX			;If yy = 0 then CX = CX - 1
		jmp	Yr_nul
Yr_not_nul:	dec	AX			;AL = yy - 1
Yr_nul: 	mov	BL,4
		div	BL			;AL = yy - 1 DIV 4
		xor	AH,AH			;Clear remainder
		add	CX,AX			;CX = daysleft + (yy - 1) DIV 4
		xor	AH,AH
		mov	AL,30
		mov	BL,Clock_cntr.Mo
		mul	BL			;AX = 30 * mm
		add	CX,AX			;CX = CX + (30 * mm)
		mov	AL,57
		mul	BL			;AX = 57 * mm
		mov	BL,100
		div	BL			;AL = (57 * mm) DIV 100
		xor	AH,AH
		add	CX,AX			;CX = CX + (57 * mm) DIV 100
		sub	CX,29			;CX = CX - 29
		mov	AL,Clock_cntr.Day
		add	CX,AX			;CX = CX + dd
		cmp	Clock_cntr.Mo,2 	;If mm > 2
		jbe	Save_result
		mov	AL,Clock_cntr.Yr	;  then ...
		test	AL,3			;Same as 'mov BL,4'
						;	 'div BL'
						;	 'cmp AH,0'
		jnz	Not_leap_yr		;If yy MOD 4 = 0
		dec	CX			;  then CX = CX - 1
		jmp	Save_result
Not_leap_yr:	sub	CX,2			;  else CX = CX - 2
Save_result:	dec	CX			;CX = CX - 1 (Adjust for DOS)
		mov	word ptr [Clk_tbl[0]],CX ;Save result
		ret
Daysconv	endp

Dateconv	proc	near
;  Convert the number of days since 1-1-80 to MM-DD-YY.
;  number of days is passedr in first 2 bytes of Clock_tbl
;  yy is returned in Clock_cntr.Yr
;  mm is returned in Clock_cntr.Mo
;  dd is returned in Clock_cntr.Day
;  AX, BX, CX, DX are used.  It is the responsibility of the caller to save
;them if they would be needed later.
;  CX = daysleft.
		mov	CX,word ptr [Clk_tbl[0]] ;CX = days - 1 (as passed
						;	by DOS)
		mov	BX,CX			;BX = days - 1
		inc	CX			;CX = days
		mov	AX,50
		mul	BX			;DX + AX = (days - 1) * 50
		mov	BX,18265
		div	BX			;AX = [(days - 1) * 50] DIV 18263
		mov	Clock_cntr.Yr,AL	;Save yy
		push	AX
		mov	BX,365
		mul	BX			;AX = yy * 365
		sub	CX,AX			;CX = days - (yy * 365)
		pop	AX			;AX = yy
		push	AX
		or	AL,AL			;Is yy = 0 ?
		jnz	Yy_not_nul		;  If yy = 0 then yy - 1 = 256.
						;  Not really what we want
		inc	CX			;CX = CX + 1
		jmp	Yy_nul
Yy_not_nul:	dec	AL			;AL = yy - 1
		mov	BL,4
		div	BL			;AL = (yy - 1) DIV 4
		xor	AH,AH
		sub	CX,AX			;CX = CX - [(yy - 1) DIV 4]
Yy_nul: 	add	CX,29			;CX = CX + 29
		pop	AX			;AX = yy
		test	AL,3			;If yy in AL is a leap year
		jnz	Yr_not_leap		;  BX = add_to_feb
		mov	BX,1			;  then BX = 1
		jmp	If_past_feb
Yr_not_leap:	mov	BX,2			;  else BX = 2
If_past_feb:	mov	AX,91
		sub	AX,BX			;AX = 91 - add_to_feb
		cmp	AX,CX			;If daysleft > 91 - add_to_feb
		jge	Cal_month
		add	CX,BX			;  then CX = CX + add_to_feb
Cal_month:	mov	AL,33
		mul	CX			;AX = 33 * CX
		mov	BX,AX			;BX = 33 * CX
		mov	AX,CX			;AX = daysleft
		mov	DL,3
		div	DL			;AL = daysleft DIV 3
		xor	AH,AH
		add	AX,BX			;AX = (33 * CX) +
						;  (daysleft DIV 3)
		xor	DX,DX
		mov	BX,1019
		div	BX			;AX = [(33 * CX) +
						;  daysleft DIV 3)] DIV 1019
		mov	Clock_cntr.Mo,AL	;Save mm
		push	AX
		mov	BL,30
		mul	BL			;AX = 30 * mm
		sub	CX,AX			;CX = CX - (30 * mm)
		pop	AX			;AL = mm
		mov	BL,57
		mul	BL			;AX = mm * 57
		mov	BL,100
		div	BL			;AL = (mm * 57) DIV 100
		sub	CL,AL			;CL = CL - (mm * 57) DIV 100
						;Daysleft should be small
						;  enough to fit in one byte
						;  i.e. CL
		mov	Clock_cntr.Day,CL	;Save dd
		mov	AL,Clock_cntr.Mo
		cmp	AL,12			;If mm > 12
		jle	Finished
		mov	Clock_cntr.Mo,1 	;  then mm = 1
		inc	Clock_cntr.Yr		;	yy = yy + 1
Finished:	ret
Dateconv	endp

Clock_access			;Defined in section 'macros'

New_year	proc	near
;  Check for a "new year" and/or update month stored in RAM.
;  The MM 58167 AN clock chip does not have a counter for the years nor does
;it provide for a way to know that a month reset to 1 has occured (after 12,
;the clock month counter resets to 1).	This subroutine corrects this
;deficiency by checking if the month stored in the clock RAM the last time
;the clock was accessed has changed.  If it has and month in counter is greater
;than month in RAM then it just updates the month.  If month in counter is less
;than month in RAM it assumes that it is a new year and increment year in RAM
;by one.
;  The AST clock programs store the month in RAM under HEX format.  For
;compatibility this subroutine assume the same format.
		mov	DX,Ram_mo_ofst
		Read			;AL = month in RAM
						;*** add 'call Dec_hex' if BCD
		mov	AH,Clock_cntr.Mo	;AH = month in clock counter
		cmp	AH,AL
		jl	New		;1 < 12 so it's a new year
		jg	Update		;It's a new month
		ret			;Same old month - go back
New:		inc	Clock_cntr.Yr	;((last year - 1980) + 1)
		mov	AL,Clock_cntr.Yr
					;Add 'call Hex_dec' if using BCD format
		mov	DX,Ram_yr_ofst
		Write
Update: 	mov	AL,Clock_cntr.Mo;Update Month_ck on RAM
					;Add 'call Hex_dec' if using BCD format
		mov	DX,Ram_mo_ofst
		Write
		ret
New_year	endp

Leap_adjust	proc	near
;  If Leap_adj is set then it is a leap year.  In a leap year 3-1-yy = 2-29-yy
;because the clock chip does not count over 28 for Feb.  If the clock counter
;date is 3-1-yy, set the corresponding clock counter arrays to 2-29-yy.  If
;the date is over 3-1-yy then decrement the number of days by 1.  If the number
;of days is over 0 then convert days into BCD, clear Leap_adj and save
;these	changes to clock.
;  This procedure assumes you will access your clock at least once in the next
;10 months after Feb, of a leap year.  If this is not the case then the day
;of the month, before Mar, of the following year will be late one day.
;  When the clock is accessed after Feb during a leap year then the day
;is decremented by 1.  If the resulting day is 0, the procedure Daysconv will
;still convert it correctly to the number of days but not saved on the clock
;chip.	If it is not 0 then the result is saved to the clock and the Leap_adj
;flag cleared.
;  The procedure is:
;  if Leap_adj = 10 then
;    if mm > 2 then
;      if (mm = 3 and dd = 1) then
;	 mm = 2
;	 dd = 29
;      else
;	 days = days - 1
;	 if days <> 0 then
;	   Leap_adj = 0
;	   Save days and Leap_adjust
;  endif

		cmp	Clock_cntr.Leap_adj,Leap_code
		jne	Leap_exit
		cmp	Clock_cntr.Mo,2
		jle	Leap_exit
		cmp	Clock_cntr.Mo,3
		jne	Back_up_1_day
		cmp	Clock_cntr.Day,1
		jne	Back_up_1_day
		mov	Clock_cntr.Mo,2
		mov	Clock_cntr.Day,29
		jmp	Leap_exit
Back_up_1_day:	dec	Clock_cntr.Day
		cmp	Clock_cntr.Day,0
		je	Leap_exit
		xor	AL,AL
		mov	DX,Ram_leap_ofst
		Write
		mov	AL,Clock_cntr.Day
		call	Hex_dec
		mov	DX,6H
		Write
Leap_exit:	ret
Leap_adjust	endp

Adjust_leap	proc	near
;  Reset Leap_adj to 0
;  If it is a leap year and the month < 3 then set the Leap_adj flag to 1
;  If it is the Feb 29th then set it to Mar 1st
		mov	Clock_cntr.Leap_adj,0
		cmp	Clock_cntr.Mo,2
		jg	Adjust_exit
		xor	AH,AH
		mov	AL,Clock_cntr.Yr
		mov	BL,4
		div	BL
		cmp	AH,0
		jne	Adjust_exit
		mov	Clock_cntr.Leap_adj,Leap_code
		cmp	Clock_cntr.Mo,2
		jne	Adjust_exit
		cmp	Clock_cntr.Day,29
		jne	Adjust_exit
		inc	Clock_cntr.Mo
		mov	Clock_cntr.Day,1
Adjust_exit:	ret
Adjust_leap	endp

Time_DOS	proc	near
;  Pass hours, minutes, seconds and 1/100ths secs to DOS Clk_tbl
		lea	DI,Clk_tbl[2]
		xor	AH,AH
		mov	AL,Clock_cntr.Mins
		stosb
		mov	AL,Clock_cntr.Hrs
		stosb
		mov	AL,Clock_cntr.Hunds
		stosb
		mov	AL,Clock_cntr.Secs
		stosb
		ret
Time_DOS	endp

Time_clock	proc	near
;  Load time from DOS Clk_tbl into Clock_cntr table
		lea	SI,Clk_tbl[2]
		xor	AH,AH
		lodsb
		mov	Clock_cntr.Mins,AL
		lodsb
		mov	Clock_cntr.Hrs,AL
		lodsb
		mov	Clock_cntr.Hunds,AL
		lodsb
		mov	Clock_cntr.Secs,AL
		ret
Time_clock	endp

Read_clock	proc	near
;  Set up registers to read clock chip and store it in Clock_cntr table.
;  Start reading from top of base address of chip (1/100,000 of seconds).
		push	CS
		pop	ES
		mov	DX,Status_bit_ofst	;Read status bit 1rst time
		Read				;  to clear it
Retry:		lea	DI,Clock_cntr
		cld
		mov	CX,No_clock_cntr
Loadit: 	mov	DX,No_clock_cntr
		sub	DX,CX
		Read
		push	CX
		call	Dec_hex 	;Convert from BCD to Hex
		stosb
		pop	CX		;Restore counter
		loop	Loadit
;  Check for counter rollover during read (Status byte = 80H)
		mov	DX,Status_bit_ofst
		Read
		test	AL,AL
		jnz	Retry
;  Store Leap_adj in RAM into Clock_cntr table
		mov	DX,Ram_leap_ofst
		Read
					;Add 'call Dec_hex' if using BCD format
		stosb
;  Store year in RAM into Clock_cntr table
		mov	DX,Ram_yr_ofst
		Read
					;Add 'call Dec_hex' if using BCD format
		stosb
;  Routines to prepare data for transfer to DOS
		call	New_year	;Check to see if it's a new year
		call	Leap_adjust
		call	Daysconv		;Number of days since 1/1/80
		call	Time_DOS		;Current time in Hex
		lea	SI,Clk_tbl
		mov	ES,CS:User_buf+2	;Set destination (ES:DI) to
		mov	DI,CS:User_buf		;caller's buffer
		push	CS
		pop	DS		;Establish source
		mov	CX,6		;6 bytes
	rep	movsb			;Send information to buffer for "read"
		ret
Read_clock	endp


Set_clock	proc near
;  Gets data from DOS, transform it into the corresponding date & sets chip.
		mov	DS,CS:User_buf+2	;Set source to caller's (DOS)
		mov	SI,CS:User_buf		;	buffer blk address
		lea	DI,Clk_tbl
		push	CS
		pop	ES
		mov	CX,6			;Set up to move data from
		cld				;	User_buf to Clk_tbl
	rep	movsb			;Do it
;  Calculate and load local clock chip table (structure)
		push	CS
		pop	DS		;Establish DS as this segment
		call	Time_clock	;Load time
		call	Dateconv	;Days to YY-MM-DD
		call	Adjust_leap
;  Writes data to chip starting from bottom of Clock_cntr table, i.e. from
;year,then leap_adj, then etc.
		lea	SI,Clock_cntr+No_clock_cntr+No_clock_RAM-1H
		std
		lodsb			;Same as 'mov AL,Clock_cntr.Yr'
					;Add 'call Hex_dec' if using BCD format
		mov	DX,Ram_yr_ofst
		Write			;Store Clock_cntr.Yr into RAM
		lodsb
					;Add 'call Hex_dec' if using BCD format
		mov	DX,Ram_leap_ofst
		Write			;Store Clock_cntr.Leap_adj into RAM
		lodsb
		mov	DX,Ram_mo_ofst
		Write			;Store Clock_cntr.Mo into RAM
		mov	CX,No_clock_cntr;Prepare registers to set counters
		xor	AH,AH
Write_cntr:	push	CX		;Next routine takes care of counters
		mov	DX,-1H
		add	DX,CX
		call	Hex_dec 	;Change to BCD
		Write			;"Write" to chip
		pop	CX
		lodsb			;Move next data to AL
		loop	Write_cntr	;Loop until done
		ret
Set_clock	endp


;-----------------------------------------------------------------------;
;  Device strategy							;
;-----------------------------------------------------------------------;
Dev_strategy:
		mov	CS:Rh_seg,ES	;Save segment of request header ptr
		mov	CS:Rh_off,BX	;Save offset of same
		ret


;-----------------------------------------------------------------------;
;  Device interrupt handler						;
;-----------------------------------------------------------------------;
Dev_int:	cld
;	Preserve machine stateM
		push	DS
		push	ES
		push	AX
		push	BX
		push	CX
		push	DX
		push	DI
		push	SI
;	Branch according to the function passed
		mov	BX,CS:Rh_off	;Get request block address from where
		mov	ES,CS:Rh_seg	;	device strategy put it
		mov	AL,ES:[BX]+2	;Get function byte
		rol	AL,1		;Get offset into table
		lea	DI,Funtab	;Get address of function table
		xor	AH,AH
		add	DI,AX
		jmp	word ptr[DI]
;	The following entries are not supported by this device
Ioctl_in:
Ioctl_out:
Nd_input:
In_stat:
In_flush:
Out_stat:
Out_flush:
Media_check:
Build_bpb:
Out_verify:
;	Set status byte as "error, incomprehensible command"
		Status	Done,Error,03
		jmp	Exit
;	Input = read clock chip
Input:		call	In_save 	;Call initial save routine
		call	Read_clock	;Read in that date/time
		mov	BX,CS:Rh_off	;Restore ES:BX as request hdr ptr
		mov	ES,CS:Rh_seg
		Status	Done,NoError,0
		jmp	Exit
;	Output = set time & date
Output: 	call	In_save 	;Call initial save routine
		call	Set_clock	;Set time & date
		mov	BX,CS:Rh_off	;Restore ES:BX as request hdr ptr
		mov	ES,CS:Rh_seg
		Status	Done,NoError,0
		jmp	Exit
;	Everybody's exit
Exit:		pop	SI
		pop	DI
		pop	DX
		pop	CX
		pop	BX
		pop	AX
		pop	ES
		pop	DS
		ret
;	Intialize
Init:		push	CS
		pop	AX			;Copy current CS to AX
		add	AX,offset Init
		mov	ES:word ptr Br_addr_0[BX],0
		mov	ES:Br_addr_1[BX],AX	;Make that the break address
;	Check verify Byte - Has some other software diddled the clock
		mov	DX,Valid_ofst		;Port for verify RAM
		Read
		cmp	AL,Valid_code		;Arbitrary validation code
		jz	Fine			;Jump on Ok
;	Reset chip, can't trust it
		mov	AL,0FFH 		;Reset all registers
		mov	DX,Cntr_reset_ofst
		Write				;Reset all counters
		inc	DX			;RAM (latch) reset address
		Write				;Now reset RAM
		mov	AL,Valid_code
		mov	DX,Valid_ofst
		Write				;Now validate RAM
;	Everything is correct
Fine:		Status	Done,NoError,0
		jmp	Exit
Clk	endp
cseg	ends
	end	Begin
ent to crash 10 times.   ˆ(You get a point every time your ¬ opponent crashes.) 