;	SCCSID = @(#)util.asm	1.1 85/04/10
;TITLE   UTIL - Handle utilities
;NAME    UTIL
;
; Handle related utilities for MSDOS 2.X.
;
;   pJFNFromHandle  written
;   SFFromHandle    written
;   SFFromSFN       written
;   JFNFree         written
;   SFNFree         written
;
;   Modification history:
;
;       Created: MZ 1 April 1983
;

[list -]
;
; get the appropriate segment definitions
;
%include "dosseg.nas"

[list +]
section DOSCODECODE
[list -]

;.xcref
%include "dossym.mac"
%include "devsym.mac"
;.cref
[list +]
;.sall

	I_need  CurrentPDB,WORD         ; current process data block location
	I_need  SFT_Addr,DWORD          ; pointer to beginning of table
	I_Need  PROC_ID,WORD            ; current process ID
	I_Need  USER_ID,WORD            ; current user ID
%if debug
	I_need  BugLev,WORD
	I_need  BugTyp,WORD
%include "bugtyp.nas"
%endif

BREAK   <pJFNFromHandle - return pointer to JFN table entry>

;
;   pJFNFromHandle - Given a handle, return the pointer to the JFN location
;       in the user's data space
;   Inputs:     BX - Handle
;   Outputs:    Carry Set
;                   AX has error code
;               Carry reset
;                   ES:DI point to the handle spot
;   Registers modified:
;               If no error, ES:DI, else AX,ES
procedure   pJFNFromHandle,NEAR
	ASSUME  DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	MOV     ES,[ss:CurrentPDB]         ; get user process data block
 assume es:PSP
	CMP     BX,[ES:PDB_JFN_Length]  ; is handle greater than allocated
	JB      JFNAdd                  ; no, get offset
	fmt     TypAccess,LevSFN,<"$p: Illegal JFN %x\n">,<BX>
	MOV     AL,error_invalid_handle ; appropriate error
ReturnCarry:
	STC                             ; signal error
	return                          ; go back
JFNAdd: LES     DI,[ES:PDB_JFN_Pointer] ; get pointer to beginning of table
 assume es:nothing
	ADD     DI,BX                   ; add in offset
ReturnNoCarry:
	CLC                             ; no holes
	return                          ; bye!
EndProc pJFNFromHandle

BREAK <SFFromHandle - return pointer (or error) to SF entry from handle>

;
; SFFromHandle - Given a handle, get JFN and then index into SF table
;
;   Input:      BX has handle
;   Output:     Carry Set
;                   AX has error code
;               Carry Reset
;                   ES:DI has pointer to SF entry
;   Registers modified: If error, AX,ES, else ES:DI
procedure   SFFromHandle,NEAR
	ASSUME  DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	CALL    pJFNFromHandle          ; get jfn pointer
	retc                            ; return if error
	CMP     BYTE PTR [ES:DI],-1     ; unused handle
	JNZ     GetSF                   ; nope, suck out SF
	fmt     TypAccess,LevSFN,<"$p: Illegal SFN $x:$x\n">,<ES,DI>
	MOV     AL,error_invalid_handle ; appropriate error
	jump    ReturnCarry             ; signal it
GetSF:
	SaveReg <BX>                    ; save handle
	MOV     BL,BYTE PTR [ES:DI]     ; get SFN
	XOR     BH,BH                   ; ignore upper half
	CALL    SFFromSFN               ; get real sf spot
	RestoreReg  <BX>                ; restore
	return                          ; say goodbye
EndProc SFFromHandle

BREAK <SFFromSFN - index into SF table for SFN>

;
; SFFromSFN - index into SF tables for SFN.
;
;   Input:      BX has SF index
;   Output:     ES:DI points to SF entry
;   Registers modified: ES:DI, BX only
procedure   SFFromSFN,NEAR
	ASSUME  DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	LES     DI,[ss:SFT_Addr]           ; get pointer to beginning of table
ScanLoop:
	CMP     BX,[ES:DI + SFCount]      ; is handle in this table?
	JB      GetOffset               ; yes, go grab it
	SUB     BX,[ES:DI + SFCount]
	LES     DI,[ES:DI + SFLink]       ; get next table segment
	CMP     DI,-1                   ; end of tables?
	JNZ     ScanLoop                ; no, try again
	STC                             ; error...
	JMP     SHORT Restore           ; go restore
GetOffset:
	SaveReg <AX>                    ; save AX
SF_Entry_struc_size equ sf_entry_struc_size	; NASM port equate
	MOV     AX,SF_Entry_struc_size        ; put it in a nice place
	MUL     BL                      ; times size
	ADD     DI,AX                   ; offset by size
	RestoreReg  <AX>                ; get world back
	ADD     DI,SFTable              ; offset into structure
	CLC                             ; no holes
Restore:
	return                          ; bye!
EndProc SFFromSFN

BREAK <JFNFree - return a jfn pointer if one is free>

;
; JFNFree - scan through the JFN table and return a pointer to a free slot
;
;   Input:  None.
;   Output: Carry Set
;               AX has error code, BX,ES,DI garbage
;           Carry Reset
;               BX has new handle, ES:DI is pointer to JFN slot
;   Registers modified: As above only.
procedure   JFNFree,NEAR
	ASSUME  DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	XOR     BX,BX                   ; try starting low
JFNScan:
	CALL    pJFNFromHandle          ; get the appropriate handle
	JC      JFNNone                 ; no more handles
	CMP     BYTE PTR [ES:DI],-1     ; free?
	JZ      JFNFound                ; yes, carry is clear
	INC     BX                      ; no, next handle
	JMP     JFNScan                 ; and try again
JFNNone:
	MOV     AL,error_too_many_open_files
JFNFound:
	return                          ; bye
EndProc JFNFree

BREAK <SFNFree - find a free SFN>

;
;   SFNFree - scan through the sf table looking for free entries
;   Inputs:     none
;   Outputs:    Carry Set - AX has error code, BX destroyed
;               Carry Clear - BX has SFN
;                   ES:DI - pointer to SFT
;                   SFT_ref_count is set to 1
;   Registers modified: none

Procedure SFNFree,NEAR
	ASSUME  DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	XOR     BX,BX                   ; start at beginning
SFNFreeLoop:
	SaveReg <BX>                    ; Next call zaps BX
	CALL    SFFromSFN               ; get the potential handle
	RestoreReg  <BX>
	JNC     SFNCheck                ; no carry, check to see if its free
	MOV     AL,error_too_many_open_files    ; appropriate error
	JMP     SFNDone
	nop	; identicalise
SFNCheck:
sf_Ref_Count equ sf_ref_count	; NASM port equate
	CMP     word [ES:DI + sf_Ref_Count],0  ; free?
%IFN DEBUG
	JZ      SFNGot                  ; yep, got return him
%ELSE
	JNZ     NoGot
	JMP     SFNGot
NoGot:
%ENDIF
	CMP     word [ES:DI + sf_ref_count],sf_busy
	JNZ     SFNNext                 ; not marked busy...
	fmt     TypAccess,LevSFN,<"$p: SFT $x:$x($x)is busy, owner $x:$x\n">,<ES,DI,BX,[ES:DI].sf_UID,[ES:DI].sf_pid>
	SaveReg <BX>
User_ID equ USER_ID	; NASM port label
	MOV     BX,[ss:User_ID]
	CMP     [ES:DI + sf_UID],BX
	JNZ     SFNNextP
Proc_ID equ PROC_ID	; NASM port label
	MOV     BX,[ss:Proc_ID]
	CMP     [ES:DI + sf_PID],BX
	JZ      SFNGotP
SFNNextP:
	fmt     TypAccess,LevSFN,<"$p: SFT unusable\n">
	RestoreReg  <BX>
SFNNext:
	INC     BX                      ; no, try next sf number
	JMP     SFNFreeLoop             ; and go until it fails
SFNGot:
	SaveReg <BX>
SFNGotP:
	CLC                             ; no error
	fmt     TypAccess,LevSFN,<"$p: SFT $x:$x($x) marked busy\n">,<ES,DI,BX>
	MOV     word [ES:DI + sf_ref_count],sf_busy    ; make sure that this is allocated
	MOV     BX,[ss:User_ID]
	MOV     [ES:DI + sf_UID],BX
	MOV     BX,[ss:Proc_ID]
	MOV     [ES:DI + sf_PID],BX
	RestoreReg  <BX>
SFNDone:
	return                          ; bye
EndProc SFNFree

END
