;	SCCSID = @(#)misc.asm	1.1 85/04/10
;TITLE MISC - Miscellanious routines for MS-DOS
;NAME  MISC
;
; Miscellaneous system calls most of which are CAVEAT
;
;   $SLEAZEFUNC
;   $SLEAZEFUNCDL
;   $GET_INDOS_FLAG
;   $GET_IN_VARS
;   $GET_DEFAULT_DPB
;   $GET_DPB
;   $DISK_RESET
;   $SETDPB
;   $Dup_PDB
;   $CREATE_PROCESS_DATA_BLOCK
;   SETMEM
;   FETCHI_CHECK
;   $GSetMediaID
;
;   Revision  history:
;
;	Created: ARR 30 March 1983
;
;	A000   version 4.00   Jan. 1988
;	A001   D490 -- Change IOCTL subfunctions from 63h, 43h to 66h , 46h

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

[list +]
section DOSCODECODE
[list -]

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

ENTRYPOINTSEG	EQU	0CH
MAXDIF		EQU	0FFFH
SAVEXIT 	EQU	10

	i_need	LASTBUFFER,DWORD
	i_need	BuffHead,DWORD
	i_need	INDOS,BYTE
	i_need	SYSINITVAR,BYTE
	i_need	CurrentPDB,WORD
	i_need	CreatePDB,BYTE
	i_need	FATBYTE,BYTE
	i_need	THISCDS,DWORD
	i_need	THISSFT,DWORD
	i_need	FETCHI_TAG,WORD 		 ; for TAG CHECK
	i_need	BUF_HASH_COUNT,WORD		 ;AN000; number of Hash Entries
	i_need	HIGH_SECTOR,WORD		 ;AN000; high word of sector #
	i_need	DOS34_FLAG,WORD 		 ;AN000;
%if debug
	I_need	BugLev,WORD
	I_need	BugTyp,WORD
%include "bugtyp.nas"
%endif

 assume nocheck, ss:DOSGROUP

BREAK <SleazeFunc -- get a pointer to media byte>

;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Return Stuff sort of like old get fat call
; Outputs:
;	DS:BX = Points to FAT ID byte (IBM only)
;		GOD help anyone who tries to do ANYTHING except
;		READ this ONE byte.
;	DX = Total Number of allocation units on disk
;	CX = Sector size
;	AL = Sectors per allocation unit
;	   = -1 if bad drive specified

	procedure   D_SLEAZEFUNC,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	MOV	DL,0

entry	D_SLEAZEFUNCDL
;Same as above except drive passed in DL (0=default, 1=A, 2=B, ...)

	context DS
	MOV	AL,DL
	invoke	GETTHISDRV		; Get CDS structure
SET_AL_RET:
;	MOV	AL,error_invalid_drive	; Assume error				;AC000;
	JC	BADSLDRIVE
	invoke	DISK_INFO
	JC	SET_AL_RET		; User FAILed to I 24
	MOV	[FATBYTE],AH
; NOTE THAT A FIXED MEMORY CELL IS USED --> THIS CALL IS NOT
; RE-ENTRANT. USERS BETTER GET THE ID BYTE BEFORE THEY MAKE THE
; CALL AGAIN
	MOV	DI,OFFSET FATBYTE wrt DOSGROUP
	XOR	AH,AH			; AL has sectors/cluster
		; Does not handle EDR-DOS 256 SpC,
		;  what does EDR-DOS do?
		; A: EDR-DOS returns 0 in al.
	invoke	get_user_stack
ASSUME	DS:NOTHING
	MOV	[SI + user_CX],CX
	MOV	[SI + user_DX],BX
	MOV	[SI + user_BX],DI
	MOV	[SI + user_DS], ss	; stash correct pointer
	return
BADSLDRIVE:
	transfer    FCB_Ret_ERR
EndProc D_SLEAZEFUNC
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Get_INDOS_Flag -- Return location of DOS critical-section flag>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Returns location of DOS status for interrupt routines
; Returns:
;	Flag location in ES:BX

	procedure   D_GET_INDOS_FLAG,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	invoke	get_user_stack
	MOV	word [SI + user_BX],OFFSET INDOS wrt DOSGROUP
	MOV	[SI + user_ES],SS
	return
EndProc D_GET_INDOS_FLAG
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Get_IN_VARS -- Return a pointer to DOS variables>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Return a pointer to interesting DOS variables This call is version
; dependent and is subject to change without notice in future versions.
; Use at risk.
	procedure   D_GET_IN_VARS,NEAR
	invoke	get_user_stack
	MOV	word [SI + user_BX],OFFSET SYSINITVAR wrt DOSGROUP
	MOV	[SI + user_ES],SS
	return
EndProc D_GET_IN_VARS
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;


BREAK <$Get_Default_DPB,$Get_DPB -- Return pointer to DPB>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
; Inputs:
;	None
; Function:
;	Return pointer to drive parameter table for default drive
; Returns:
;	DS:BX points to the DPB
;	AL = 0 If OK, = -1 if bad drive (call 50 only)

	procedure   D_GET_DEFAULT_DPB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	MOV	DL,0

	entry	D_GET_DPB
; Same as above only drive passed in DL (0=default, 1=A, 2=B, ...)

	context DS
	MOV	AL,DL
	invoke	GETTHISDRV		; Get CDS structure
	JC	ISNODRV 		; no valid drive
	LES	DI,[THISCDS]		; check for net CDS
	TEST	word [ES:DI + curdir_flags],curdir_isnet
	JNZ	ISNODRV 		; No DPB to point at on NET stuff
	EnterCrit CritDisk
	invoke	FATRead_CDS		; Force Media Check and return DPB
	LeaveCrit CritDisk
	JC	ISNODRV 		; User FAILed to I 24, only error we
					;   have.
	invoke	get_user_stack
ASSUME	DS:NOTHING
	MOV	[SI + user_BX],BP
	MOV	[SI + user_DS],ES
	XOR	AL,AL
	return

ISNODRV:
	MOV	AL,-1
	return
EndProc D_GET_Default_dpb
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;


BREAK <$Disk_Reset -- Flush out all dirty buffers>

	procedure   D_DISK_RESET,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

; Inputs:
;	None
; Function:
;	Flush and invalidate all buffers
; Returns:
;	Nothing

	MOV	AL,-1
 entry D_DISK_RESET2
	context DS
	EnterCrit   critDisk
	OR	word [DOS34_FLAG],FROM_DISK_RESET					;AN000;
	invoke	FLUSHBUF
	AND	word [DOS34_FLAG],NO_FROM_DISK_RESET 				;AN000;
;
; We will "ignore" any errors on the flush, and go ahead and invalidate.  This
; call doesn't return any errors and it is supposed to FORCE a known state, so
; let's do it.
;
; Invalidate 'last-buffer' used
;
	MOV	BX,-1
	MOV	WORD PTR [LASTBUFFER+2],BX
	MOV	WORD PTR [LASTBUFFER],BX
;
;	TEST	[DOS34_FLAG],IFS_DRIVE_RESET	 ;AN000;;IFS. from ifs call back ?
;	JZ	FreeDone			 ;AN000;;IFS. no
;	AND	[DOS34_FLAG],NO_IFS_DRIVE_RESET  ;AN000;;IFS. clear the flag
;	LeaveCrit   critDisk			 ;AN000;;IFS.
;	return					 ;AN000;;IFS. return
FreeDone:
	LeaveCrit   critDisk
	MOV	AX,-1
multNET equ MultNET	; NASM port equate
	CallInstall NetFlushBuf,multNET,32
	return
EndProc D_DISK_RESET

BREAK <$SetDPB - Create a valid DPB from a user-specified BPB>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
	procedure   D_SETDPB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

; Inputs:
;	ES:BP Points to DPB
;	DS:SI Points to BPB
; Function:
;	Build a correct DPB from the BPB
; Outputs:
; ES:BP and DS preserved all others destroyed

	MOV	DI,BP
	scasw				; Skip over dpb_drive and dpb_UNIT
	LODSW
	STOSW				; dpb_sector_size
	test ax, ax
	jz .nonfat
	CMP	BYTE PTR [SI + BPFTCNT-2],0     ; FAT file system drive		;AN000;
	JNZ	yesfat			      ; yes				;AN000;
.nonfat:
	MOV	BYTE PTR [ES:DI + dpb_FAT_count-4],0
	JMP	setend			      ; NO				;AN000;
	nop	; identicalise
yesfat:
	MOV	DX,AX
	LODSB				; get amount clusters (0 means 256)
	dec ax
	STOSB				; dpb_cluster_mask
	mov ah, 0
	inc ax				; translate EDR-DOS 0 to 256
	mov bl, 0			; bl = shift count
LOG2LOOP:
	TEST	AL,1
	JNZ	SAVLOG
	inc bl				; increment shift count
	shr ax, 1			; shift right amount clusters
	JMP	SHORT LOG2LOOP
SAVLOG:
	mov al, bl			; bl = al = shift count
	STOSB				; dpb_cluster_shift
	MOVSW				; dpb_first_FAT Start of FAT (# of reserved sectors)
	LODSB
	STOSB				; dpb_FAT_count Number of FATs
;	OR	AL,AL			; NONFAT ?				;AN000;
;	JZ	setend			; yes, don't do anything                ;AN000;
	MOV	BH,AL
	LODSW
	STOSW				; dpb_root_entries Number of directory entries
	MOV	CL,5
	SHR	DX,CL			; Directory entries per sector
	DEC	AX
	ADD	AX,DX			; Cause Round Up
	MOV	CX,DX
	XOR	DX,DX
	DIV	CX
	MOV	CX,AX			; Number of directory sectors
	INC	DI
	INC	DI			; Skip dpb_first_sector
	MOVSW				; Total number of sectors in DSKSIZ (temp as dpb_max_cluster)
	LODSB
	MOV	[ES:BP + dpb_media],AL	; Media byte
	LODSW				; Number of sectors in a FAT
	STOSW				;AC000;;>32mb dpb_FAT_size
	MOV	DL,BH			;AN000;;>32mb
	XOR	DH,DH			;AN000;;>32mb
	MUL	DX			;AC000;;>32mb Space occupied by all FATs
	ADD	AX,[ES:BP + dpb_first_FAT]
	STOSW				; dpb_dir_sector
	ADD	AX,CX			; Add number of directory sectors
	MOV	[ES:BP + dpb_first_sector],AX

	mov cl, bl			; cluster size shift count
	CMP	WORD PTR [ES:BP + DSKSIZ],0	;F.C. >32mb			;AN000;
	JNZ	normal_dpb	       ;F.C. >32mb				;AN000;
	XOR	CH,CH		       ;F.C. >32mb				;AN000;
BPB_BigTotalSectors equ BPB_BIGTOTALSECTORS	; NASM port label
BPB_SectorsPerTrack equ BPB_SECTORSPERTRACK	; NASM port label
	MOV	BX,WORD PTR [SI+BPB_BigTotalSectors-BPB_SectorsPerTrack]	;AN000;
	MOV	DX,WORD PTR [SI+BPB_BigTotalSectors-BPB_SectorsPerTrack+2]	;AN000;
	SUB	BX,AX		       ;AN000;;F.C. >32mb
	SBB	DX,0		       ;AN000;;F.C. >32mb
	jcxz	norot		       ;AN000;;F.C. >32mb
rott:				       ;AN000;;F.C. >32mb
	shr	DX,1		       ;AN000;;F.C. >32mb
	RCR	BX,1		       ;AN000;;F.C. >32mb
	LOOP	rott		       ;AN000;;F.C. >32mb
norot:				       ;AN000;
	MOV	AX,BX		       ;AN000;;F.C. >32mb
	JMP	setend		       ;AN000;;F.C. >32mb
	nop	; identicalise
normal_dpb:
	SUB	AX,[ES:BP + DSKSIZ]
	NEG	AX			; Sectors in data area
;;	MOV	CL,BL			; dpb_cluster_shift
	SHR	AX,CL			; Div by sectors/cluster
setend:
	INC	AX
	MOV	[ES:BP + dpb_max_cluster],AX
	MOV	word [ES:BP + dpb_next_free],0 ; Init so first ALLOC starts at
					; begining of FAT
	MOV	word [ES:BP + dpb_free_cnt],-1 ; current count is invalid.
	return
EndProc D_SETDPB
;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;

BREAK <$Create_Process_Data_Block,SetMem -- Set up process data block>
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;									   ;
;
; Inputs:   DX is new segment address of process
;	    SI is end of new allocation block
;
	procedure   D_Dup_PDB,NEAR
ASSUME	DS:NOTHING,ES:NOTHING
	MOV	byte [ss:CreatePDB],0FFH		; indicate a new process
	MOV	DS,[ss:CurrentPDB]
 assume ds:PSP
	PUSH	SI
	JMP	SHORT	CreateCopy
EndProc D_Dup_PDB

	procedure   D_CREATE_PROCESS_DATA_BLOCK,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

; Inputs:
;	DX = Segment number of new base
; Function:
;	Set up program base and copy term and ^C from int area
; Returns:
;	None
; Called at DOS init

	CALL	get_user_stack
	MOV	DS,[SI + user_CS]
 assume ds:PSP
PDB_Block_len equ PDB_block_len	; NASM port label
	PUSH	word [PDB_Block_len]
CreateCopy:
	MOV	ES,DX
 assume es:nothing
	XOR	SI,SI			; copy all 80h bytes
	MOV	DI,SI
	MOV	CX,80H
	REP	MOVSW
; DOS 3.3 7/9/86

	MOV	CX,FilPerProc		; copy handles in case of
	MOV	DI,offset PDB_JFN_Table	; Set Handle Count has been issued
	PUSH	DS
	LDS	SI,[PDB_JFN_Pointer]
 assume ds:nothing
	REP	MOVSB
	POP	DS
 assume ds:PSP

; DOS 3.3 7/9/86
	TEST	byte [ss:CreatePDB],0FFh; Shall we create a process?
	JZ	Create_PDB_cont 	; nope, old style call
;
; Here we set up for a new process...
;

	PUSH	ss
	POP	DS
	DOSAssume   CS,<DS>,"MISC/Create_Copy"
	XOR	BX,BX			; dup all jfns
	MOV	CX,FilPerProc		; only 20 of them

Create_dup_jfn:
	PUSH	ES			; save new PDB
	invoke	SFFromHandle		; get sf pointer
	MOV	AL,-1			; unassigned JFN
	JC	CreateStash		; file was not really open
	TEST	word [ES:DI + sf_flags],sf_no_inherit
	JNZ	CreateStash		; if no-inherit bit is set, skip dup.
;
; We do not inherit network file handles.
;
	MOV	AH,BYTE PTR [ES:DI + sf_mode]
	AND	AH,sharing_mask
sharing_net_fcb equ sharing_net_FCB	; NASM port equate
	CMP	AH,sharing_net_fcb
	jz	CreateStash
;
; The handle we have found is duplicatable (and inheritable).  Perform
; duplication operation.
;
	MOV	WORD PTR [THISSFT],DI
	MOV	WORD PTR [THISSFT+2],ES
	invoke	DOS_DUP 		; signal duplication
;
; get the old sfn for copy
;
	invoke	pJFNFromHandle		; ES:DI is jfn
	MOV	AL,[ES:DI]		; get sfn
;
; Take AL (old sfn or -1) and stash it into the new position
;
CreateStash:
	POP	ES
 assume es:nothing
	MOV	[ES:BX + PDB_JFN_Table],AL; copy into new place!
	INC	BX			; next jfn...
create_dup_jfn equ Create_dup_jfn	; NASM port label
	LOOP	create_dup_jfn

	MOV	BX,[CurrentPDB]		; get current process
	MOV	[ES:PDB_Parent_PID],BX	; stash in child
	MOV	[CurrentPDB],ES
	MOV	DS,BX
	ASSUME	DS:NOTHING
;
; end of new process create
;
Create_PDB_cont:
	MOV	BYTE PTR [ss:CreatePDB],0h ; reset flag
	POP	AX

	entry	SETMEM			; called from NEARDOSINIT, ss != DOSDATA
ASSUME	DS:NOTHING,ES:NOTHING,SS:NOTHING

; Inputs:
;	AX = Size of memory in paragraphs
;	DX = Segment
; Function:
;	Completely prepares a program base at the
;	specified segment.
; Called at DOS init
; Outputs:
;	DS = DX
;	ES = DX
;	[0] has INT int_abort
;	[2] = First unavailable segment
;	[5] to [9] form a long call to the entry point
;	[10] to [13] have exit address (from int_terminate)
;	[14] to [17] have ctrl-C exit address (from int_ctrl_c)
;	[18] to [21] have fatal error address (from int_fatal_abort)
; DX,BP unchanged. All other registers destroyed.

	XOR	CX,CX
	MOV	DS,CX
 assume ds:IVT
	MOV	ES,DX
 assume es:nothing
	MOV	SI,addr_int_terminate
	MOV	DI,SAVEXIT
	MOV	CX,6
	REP	MOVSW
	MOV	[ES:2],AX
	SUB	AX,DX
	CMP	AX,MAXDIF
	JBE	HAVDIF
	MOV	AX,MAXDIF
HAVDIF:
	SUB	AX,10H			; Allow for 100h byte "stack"
	MOV	BX,ENTRYPOINTSEG	;	in .COM files
	SUB	BX,AX
	MOV	CL,4
	SHL	AX,CL
	MOV	DS,DX
 assume ds:nothing
	MOV	WORD PTR [PDB_CPM_Call+1],AX
	MOV	WORD PTR [PDB_CPM_Call+3],BX
	MOV	word [PDB_Exit_Call],(int_abort << 8) + mi_INT
	MOV	BYTE PTR [PDB_CPM_Call],mi_Long_CALL
PDB_Call_System equ PDB_Call_system	; NASM port label
	MOV	WORD PTR [PDB_Call_System],(int_command << 8) + mi_INT
	MOV	BYTE PTR [PDB_Call_System+2],mi_Long_RET
	MOV	WORD PTR [PDB_JFN_Pointer],PDB_JFN_Table
	MOV	WORD PTR [PDB_JFN_Pointer+2],DS
	MOV	WORD PTR [PDB_JFN_Length],FilPerProc
;
; The server runs several PDB's without creating them VIA EXEC.  We need to
; enumerate all PDB's at CPS time in order to find all references to a
; particular SFT.  We perform this by requiring that the server link together
; for us all sub-PDB's that he creates.  The requirement for us, now, is to
; initialize this pointer.
;
	or	word ptr [PDB_Next_PDB],-1
	or	word ptr [PDB_Next_PDB+2],-1
	return

EndProc D_CREATE_PROCESS_DATA_BLOCK

;									   ;
;	     C	A  V  E  A  T	  P  R	O  G  R  A  M  M  E  R		   ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
	procedure   FETCHI_CHECK,NEAR
ASSUME	DS:NOTHING,ES:NOTHING,SS:DOSGROUP
	PUSHF
	CMP	word [ss:FETCHI_TAG],22642
	JZ	TAG_OK
	invoke	TAGCRASHDOS	      ; go to hell
TAG_OK:
	POPF
	return
EndProc FETCHI_CHECK

BREAK <$GSetMediaID -- get set media ID>
; Inputs:
;	BL= drive number as defined in IOCTL
;	AL= 0 get media ID
;	    1 set media ID
;	DS:DX= buffer containing information
;		DW  0  info level (set on input)
;		DD  ?  serial #
;		DB  11 dup(?)  volume id
;		DB   8 dup(?)  file system type
; Function:
;	Get or set media ID
; Returns:
;	carry clear, DS:DX is filled
;	carry set, error

	procedure   D_GSetMediaID,NEAR ;AN000;
ASSUME	DS:NOTHING,ES:NOTHING, ss:DOSGROUP

	MOV	CX,0866H	      ;AN000;MS.; assume get  for IOCTL
	CMP	AL,0		      ;AN001;MS.; get ?
	JZ	doioctl 	      ;AN000;MS.; yes
	CMP	AL,1		      ;AN000;MS.; set ?
	JNZ	errorfunc	      ;AN000;MS.; no
	MOV	CX,0846H	      ;AN001;MS.;
doioctl:			      ;AN000;
	MOV	AL,0DH		      ;AN000;MS.; generic IOCTL
	invoke	D_IOCTL		      ;AN000;MS.; let IOCTL take care of it
	return			      ;AN000;MS.;
errorfunc:			      ;AN000;
	error	error_invalid_function;AN000;MS.	; invalid function
EndProc D_GSetMediaID		      ;AN000;

END
