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 ; 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
; 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
; call set_jmp ; ; returns AX = 0 ; ; long_jmp: ; calling mechanism is: push ; push
; 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