	title	UNIXCLIB - UNIX/C FUNCTIONS FOR PC/MS-DOS
	page	55,131

; UNIXCLIB - Routines for providing UNIX-like functions for PC/MS-DOS
;
; Date of last update: 10/9/85
;
; This is a package of routines that 
;
;	(1) emulate routines found in the standard C function libraries on
;		UNIX systems, and/or
;	(2) help bridge the gap between MS-DOS and Unix.
;
;
; Author credit summary:
;
;	Function		Author			Organization
;
;	sleep			Lawrence B. Afrin	Clemson University
;	file_exists			"			"
;	set_jmp				"			"
;	long_jmp			"			"
;
;
; This package was conceived by Lawrence B. Afrin of Clemson University.
; Please send all questions, comments, suggestions, and new routines to
; LBAFRIN@CLEMSON (CSNet) or LBAFRIN.CLEMSON@CSNET-RELAY (ARPANet).  These
; routines were principally written for support of DeSmet C, although
; they can probably be easily interfaced to any C compiler if you know
; the compiler's mechanism(s) or protocol(s) for calling assembler
; routines.
;
; The current catalog of routines in UNIXCLIB.ASM follows:
;
;	sleep(x)		functionally equivalent to UNIX sleep(3) -
;		int x		pauses execution for x seconds
;
;	file_exists(s)		determines whether the named PC/MS-DOS file
;		char *s		exists - helps in building support for the
;				UNIX System V O_CREAT feature (create/open
;				file only if it doesn't already exist)
;
;	long_jmp(j)		major enhancement to DeSmet longjmp() -
;		jmp_buf j;	provides functionality identical to the
;				standard UNIX longjmp() (which DeSmet doesn't)
;
;	set_jmp(j)		major enhancement to DeSmet setjmp() -
;		jmp_buf j;	provides functionality identical to the
;				standard UNIX setjmp() (which DeSmet doesn't)
;
;
; Remember, send your contributions to this library to LBAFRIN@CLEMSON.
; I'm happy to receive DeSmet-related and non-DeSmet-related routines for
; review.  I can usually respond to submissions within a day or two, but
; please don't complain if you see my response time lagging.  As a
; medical student I have to put my academic and patient responsibilities
; at a higher priority than the public service of supervising this library.
;
;						-- Larry Afrin
;						   Dept. of Computer Science
;						   Clemson University



; Implementation notes for the following 4 routines:  All were tested under
; MASM v1.00, DeSmet C 2.41, and PC-DOS 3.10.  Most versions of MASM and
; DeSmet C should work.  I expect that PC/MS-DOS 2.x and 3.0 work fine,
; too, but I don't think 1.10 will hack it.

;
; DeSmet puts all code segments in the `pgroup' group and all data segments
; in the `dgroup' group.
;
pgroup	group	prog
dgroup	group	data

prog	segment	para public 'prog'
	assume	cs:prog,ds:data,es:nothing

	public	sleep
	public	file_exists
	public	set_jmp
	public	long_jmp

;
; sleep - written by Lawrence B. Afrin of Clemson University
; (lbafrin@clemson).  Provides UNIX-like sleep(3) functionality.
;
;	calling mechanism is:	push	<number of seconds to sleep>
;				call	sleep
;
;	note that this limits the maximum sleep time to 65535 seconds
sleep	proc	near
	push	bp			; enter in a DeSmet-approved (more or
	mov	bp,sp			;   less) manner
	sub	sp,10
	push	ax			; save regs
	push	bx
	push	cx
	push	dx
	cmp	ax,0			; test for sleep(x) where x <= 0
	jg	sleep8
	jmp	sleep7			; ignore call if such is the case
sleep8:	mov	ah,2ch			; get the current time from DOS
	int	21h
	mov	[bp-2],cx		; [bp-4:bp-2] hold the original time
	mov	[bp-4],dx
	mov	[bp-6],cx		; [bp-8:bp-6] will hold the target
	mov	[bp-8],dx		;   time (when we end our sleep)
	mov	word ptr [bp-10],60
	mov	ax,[bp+4]
	xor	dx,dx
	div	word ptr [bp-10]
	add	dl,[bp-7]		; add num of secs (mod 60) to orig secs
	cmp	dl,60
	jl	sleep0
	sub	dl,60			; overflow of targ secs, so take mod 60
	inc	byte ptr [bp-6]		;   and increment target minutes
sleep0:	mov	[bp-7],dl
	cmp	ax,0
	je	sleep3
	xor	dx,dx
	div	word ptr [bp-10]
	add	dl,[bp-6]		; add num of mins (mod 60) to targ mins
	cmp	dl,60
	jl	sleep1
	sub	dl,60			; overflow of targ mins, so take mod 60
	inc	byte ptr [bp-5]		;   and increment target hours
sleep1:	mov	[bp-6],dl
	cmp	ax,0
	je	sleep3
	add	al,[bp-5]
	cmp	al,24
	jl	sleep2
	sub	al,24
sleep2:	mov	[bp-5],al		; by now we know our target time
					; now let's start looping until we hit
					;   the target time
sleep3:	mov	ah,2ch			; top of loop: get current time
	int	21h
	mov	ax,[bp-6]
	mov	bx,[bp-8]
	cmp	cx,[bp-2]	; if current time >= original time (don't have
	jg	sleep4		;   to worry about 24 hour wraparound because
	jl	sleep6		;   max number of sleep secs = 65535, which
	cmp	dx,[bp-4]	;   is < 24 hours (= 86400))
	jl	sleep6
sleep4:	cmp	ax,[bp-2]	;   then if target time <= original time
	jg	sleep5
	jl	sleep3
	cmp	bx,[bp-4]
	jle	sleep3		;          then loop again
sleep5:	cmp	cx,ax		;          else if current time >= target time
	jg	sleep7		;                 then exit
	jl	sleep3		;                 else loop again
	cmp	dx,bx
	jl	sleep3
	jmp	sleep7
sleep6:	cmp	ax,[bp-2]	;   else if target time > original time
	jg	sleep7
	cmp	bx,[bp-4]
	jg	sleep7		;          then exit
	cmp	cx,ax		;          else if current time >= target time
	jg	sleep7		;                 then exit
	jl	sleep3		;                 else loop again
	cmp	dx,bx
	jl	sleep3
sleep7:	pop	dx		; exit
	pop	cx
	pop	bx
	pop	ax
	mov	sp,bp
	pop	bp
	ret
sleep	endp


;
; file_exists - written by Lawrence B. Afrin of Clemson University
; (lbafrin@clemson).  Tells caller whether specified PC/MS-DOS file
; already exists.  Helpful in building higher level file system support
; such as for the O_CREAT flag in the UNIX System V open(2) call.
;
;	calling mechanism is:	push	<address (in data seg) of filename string>
;				call	file_exists
;
;	returns AX = 0 if file does not exist
;		AX = 1 if file does exist
;
; Note that file_exists temporarily establishes a new Disk Transfer Address.
;

data	segment	para public 'data'

file_exists_dta	db	128 dup (?)
old_dta_seg	dw	?
old_dta_off	dw	?

data	ends

file_exists	proc	near
	push	bp			; play DeSmet games
	mov	bp,sp
	push	bx
	push	cx
	push	dx
	push	es
	mov	ah,2fh			; get old dta
	int	21h
	mov	old_dta_seg,es		; save old dta
	mov	old_dta_off,bx
	mov	dx,offset dgroup:file_exists_dta
	mov	ah,1ah
	int	21h			; set up temp dta
	mov	dx,[bp+4]
	mov	ah,4eh
	mov	cx,0
	int	21h			; ask DOS if file exists
	mov	cx,1			; assume it does
	cmp	ax,0			; does it?
	je	file_exists_x		; yup
	mov	cx,0			; whoops, no it doesn't
file_exists_x:
	push	cx			; save return code
	push	ds
	mov	dx,old_dta_off
	mov	ax,old_dta_seg
	mov	ds,ax
	mov	ah,1ah
	int	21h			; restore old dta
	pop	ds
	pop	ax			; restore return code
	pop	es
	pop	dx
	pop	cx
	pop	bx
	mov	sp,bp
	pop	bp
	ret
file_exists	endp


; set_jmp and long_jmp - written by Lawrence B. Afrin of Clemson University
; (lbafrin@clemson).  Provides UNIX-like setjmp(3) and longjmp(3) function-
; ality, significantly extends functionality of DeSmet counterparts.
;
; Note that the type of the "jmp_buf"-type variable in this implementation
; is a three-element one-dimensional integer array.  (The C statement would be
; "typedef int jmp_buf[3];".
;
; set_jmp:
;	calling mechanism is:	push	<address (in data seg) of jmp_buf var>
;				call	set_jmp
;
;	returns AX = 0
;
; long_jmp:
;	calling mechanism is:	push	<value to be faked as set_jmp return val>
;				push	<address (in data seg) of jmp_buf var>
;				call	long_jmp
;
;	causes a return with AX = "fake return val" to the instruction follow-
;	ing the call to set_jmp that was made specifying the same jmp_buf
;	variable
;
; IMPORTANT NOTES!!!
;
;	(1) These routines are specific for DeSmet C and probably no other C
;		compilers.  They depend heavily on the DeSmet protocol used
;		to manage stack frames.
;	(2) The same warnings as in the DeSmet manual apply here.  A long_jmp
;		into a routine containing the corresponding set_jmp, when the
;		target routine is no longer "active" (i.e., on the stack), has
;		a totally unguaranteed result.
;	(3) The performance of these routines under the DeSmet debugger has
;		not been tested.  Use at your own risk!
;
;
; How It Works
;
;	The DeSmet "environment" that needs to be saved and restored by
;	set_jmp/long_jmp consists of an address to jump/return to, the
;	current frame pointer (found in BP), and the current stack
;	pointer (you guessed it, SP).  set_jmp saves these values into
;	the jmp_buf, and long_jmp restores SP and BP and then plays a
;	trick so that when it returns, it actually is as if the
;	corresponding set_jmp were returning (except that the return value
;	is as specified in the call to long_jmp).
;

set_jmp	proc	near
	push	bp		; we're going to do this without disturbing
	push	bx		;   regs except the return value reg, AX
	mov	bp,sp		; let's get some addressability here
	mov	bx,[bp+6]	; load the jmp_buf address into BX
	mov	ax,[bp+4]	; load the target jump address into AX
	mov	[bx],ax		; tuck this address away in (int) jmp_buf[0]
	lea	ax,[bp+6]	; load into AX the value that we're going to
				;   want to give SP in the long_jmp
	mov	[bx+2],ax	; tuck that away in (int) jmp_buf[1]
	mov	ax,[bp+2]	; load into AX the BP (frame ptr) we saved above
	mov	[bx+4],ax	; save that, too, in (int) jmp_buf[2]
	pop	bx		; that's it!  now restore regs,
	pop	bp
	mov	ax,0		; set the return code
	ret			; and get out of here
set_jmp	endp

long_jmp	proc	near	; notice that we get away with this without
				;   having to play with any registers not
				;   intimately involved in the concept of
				;   a long_jmp; neat, huh?
	mov	bp,sp		; use bp for work variable
	mov	ax,[bp+4]	; load long_jmp's 2nd argument as the return val
	mov	bp,[bp+2]	; now point bp at the jmp_buf
	mov	sp,ds:[bp+2]	; load SP
	push	ds:[bp+0]	; push the target address onto the `new' stack
	mov	bp,ds:[bp+4]	; load BP
				; (The stack should now be looking *exactly*
				; as if it's ready for a NEAR-type return
				; from the corresponding set_jmp call, except
				; that AX has the return value specified in
				; the long_jmp call.)
	ret			; let's get back to work
long_jmp	endp

prog	ends

	end
