;****************************************************************************
;*									    *
;*  PMDOS.ASM - 							    *
;*									    *
;*	Low level DOS routines needed by the Program Manager.		    *
;*									    *
;****************************************************************************

memS=1
?PLM=1
?WIN=1

include cmacros.inc

SelectDisk		equ 0Eh
FCBFindFirstFile	equ 11h
FCBFindNextFile 	equ 12h
FCBDeleteFile		equ 13h
FCBRenameFile		equ 17h
GetCurrentDisk		equ 19h
SetDTAAddress		equ 1Ah
GetFileSize		equ 23h
GetDiskFreeSpace	equ 36h
CreateDir		equ 39h
RemoveDir		equ 3Ah
ChangeCurrentDir	equ 3Bh
CreateFile		equ 3Ch
DeleteFile		equ 41h
GetSetFileAttributes	equ 43h
GetCurrentDir		equ 47h
FindFirstFile		equ 4Eh
FindNextFile		equ 4Fh
RenameFile		equ 56h

externFP DOS3CALL

externFP    AnsiToOem
ifdef	DBCS
externFP    IsDBCSLeadByte
endif

;=============================================================================

createSeg _%SEGNAME, %SEGNAME, WORD, PUBLIC, CODE

sBegin %SEGNAME

assumes CS,%SEGNAME
assumes DS,DATA

cProc	PathType, <FAR, PUBLIC>

    parmD   lpFile

    localV  szOEM, 128

cBegin

    RegPtr lpszOEM, ss, bx
    lea     bx, szOEM
    cCall   AnsiToOem, <lpFile, lpszOEM>

    lea     dx, szOEM
    mov     ax, 4300h
    call    DOS3CALL
    jnc     id_noerror
    xor     ax, ax
    jmp     short id_exit

id_noerror:
    and     cx, 10h
    jnz     id_dir
    mov     ax, 1
    jmp     short id_exit

id_dir:
    mov     ax, 2

id_exit:
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  FileTime*() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc FileTime, <FAR, PUBLIC>

    parmW   hFile

cBegin
    mov     ax, 5700h
    mov     bx, hFile
    cCall   DOS3CALL
    jnc     ft_ok
    sub     ax, ax
    sub     dx, dx
    jmp     short ft_ex
ft_ok:
    mov     ax, cx
ft_ex:
cEnd

;*--------------------------------------------------------------------------*
;*									    *
;*  IsReadOnly() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc IsReadOnly, <FAR, PUBLIC>

    parmD   lpFile

    localV  szOEM, 128

cBegin

    RegPtr  lpszOEM, ss, bx
    lea     bx, szOEM
    cCall   AnsiToOem,<lpFile,lpszOEM>

    mov     ax, 4300h		; get attributes...
    lea     dx, szOEM		; ...for given file...
    call    DOS3CALL
    jc	    f_exit		; ax == 0 if error...
    and     ax, 1		; test RO bit
    jmp     short t_exit        ; true
f_exit:
    xor     ax, ax              ; false
t_exit:
cEnd



;*--------------------------------------------------------------------------*
;*									    *
;*  GetDOSErrorCode() - 						    *
;*									    *
;*--------------------------------------------------------------------------*

cProc GetDOSErrorCode, <FAR, PUBLIC>, <SI,DI>

cBegin

    mov     ah,59h		; Get DOS extended error
    sub     bx,bx		; cause ray duncan says so
    push    ds			; function trashes registers
    push    bp			; so we gotta save 'em for cProc
    call    DOS3CALL
    pop     bp			; be nice to C
    pop     ds			; get DS
    mov     dl,bh		; class in byte 2
    mov     dh,ch		; locus in byte 3 (skip the rec. action)

cEnd

;*--------------------------------------------------------------------------*
;*									    *
;*  GetCurrentDrive() - 						    *
;*									    *
;*--------------------------------------------------------------------------*

cProc GetCurrentDrive, <FAR, PUBLIC>

cBegin
	    mov     ah,GetCurrentDisk
	    call    DOS3CALL
	    sub     ah,ah		; Zero out AH
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  SetCurrentDrive() - 						    *
;*									    *
;*--------------------------------------------------------------------------*

; Returns the number of drives in AX.

cProc SetCurrentDrive, <FAR, PUBLIC>

ParmW Drive

cBegin
	    mov     dx,Drive
	    mov     ah,SelectDisk
	    call    DOS3CALL
	    sub     ah,ah		; Zero out AH
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  GetCurrentDirectory() -						    *
;*									    *
;*--------------------------------------------------------------------------*

cProc GetCurrentDirectory, <FAR, PUBLIC>, <SI, DI>

parmW wDrive
ParmD lpDest

cBegin
	    push    ds			; Preserve DS

	    mov     ax,wDrive
	    or	    al,al
	    jnz     GCDHaveDrive

	    call    GetCurrentDrive

	    inc     al			; Convert to logical drive number

GCDHaveDrive:
	    les     di,lpDest		; ES:DI = lpDest
	    push    es
	    pop     ds			; DS:DI = lpDest
	    cld

	    mov     dl,al		; DL = Logical Drive Number
	    add     al,'@'		; Convert to ASCII drive letter
	    stosb
	    mov     al,':'
	    stosb
	    mov     al,'\'		; Start string with a backslash
	    stosb
	    mov     byte ptr es:[di],0	; Null terminate in case of error
	    mov     si,di		; DS:SI = lpDest[1]
	    mov     ah,GetCurrentDir
	    call    DOS3CALL
	    jc	    GCDExit		; Skip if error
	    xor     ax,ax		; Return FALSE if no error
GCDExit:
	    pop     ds			; Restore DS
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  SetCurrentDirectory() -						    *
;*									    *
;*--------------------------------------------------------------------------*

cProc SetCurrentDirectory, <FAR, PUBLIC>, <DS, DI>

ParmD lpDirName

cBegin
	    lds     di,lpDirName	; DS:DI = lpDirName

	    ; Is there a drive designator?
ifdef	DBCS
	    mov     al, byte ptr [di]	; fetch a first byte of path
	    xor     ah,ah
	    cCall   IsDBCSLeadByte, <ax>
	    test    ax,ax		; DBCS lead byte?
	    jnz     SCDNoDrive		; jump if so
endif
	    cmp     byte ptr [di+1],':'
	    jne     SCDNoDrive		; Nope, continue
	    mov     al,byte ptr [di]	; Yup, change to that drive
	    sub     ah,ah
	    and     al, 0DFH	 ;Make drive letter upper case
	    sub     al,'A'
	    push    ax
	    call    SetCurrentDrive
	    inc     di			; Move past the drive letter
	    inc     di			;   and colon

	    ; Are we at the end of the string?
	    mov     al,byte ptr [di]	; Are we at the end of the string?
	    cbw
	    or	    ax,ax
	    jz	    SCDExit		; Yup, exit
SCDNoDrive:
	    mov     dx,di
	    mov     ah,ChangeCurrentDir
	    call    DOS3CALL
	    jc	    SCDExit		; Skip on error
	    xor     ax,ax		; Return FALSE if successful
SCDExit:
cEnd


if 0
;*--------------------------------------------------------------------------*
;*									    *
;*  IsRemovableDrive() -						    *
;*									    *
;*--------------------------------------------------------------------------*

cProc IsRemovableDrive, <FAR, PUBLIC>

ParmW wDrive

cBegin
	    mov     ax,4408h	; IOCTL: Check If Block Device Is Removable
	    mov     bx,wDrive
	    inc     bx
	    call    DOS3CALL
	    and     ax,1	; Only test bit 0
	    xor     ax,1	; Flip so 1 == Removable
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  IsRemoteDrive() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc IsRemoteDrive, <FAR, PUBLIC>

ParmW wDrive

cBegin
	    mov     ax,4409h	; IOCTL: Check If Block Device Is Remote
	    mov     bx,wDrive
	    inc     bx
	    call    DOS3CALL
	    xor     ax,ax
	    and     dx,0001000000000000b    ; Test bit 12
	    jz	    IRDRet
	    mov     ax,1
IRDRet:
cEnd
endif

;*--------------------------------------------------------------------------*
;*									    *
;*  DosDelete() -							    *
;*									    *
;*--------------------------------------------------------------------------*

%out assuming SS==DS

cProc DosDelete, <FAR, PUBLIC>

ParmD lpSource

localV	szOEM, 128

cBegin

	    RegPtr  lpszOEM,ss,bx   ; convert path to OEM chars
	    lea     bx, szOEM
	    cCall   AnsiToOem, <lpSource, lpszOEM>

	    lea     dx, szOEM
	    mov     ah,DeleteFile   ;
	    call    DOS3CALL
	    jc	    DDExit
	    xor     ax,ax	    ; Return 0 if successful
DDExit:
cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  DosRename() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc DosRename, <FAR, PUBLIC>, <DI>

ParmD lpSource
ParmD lpDest

localV	szOEM1, 128
localV	szOEM2, 128

cBegin
	    RegPtr  lpszOEM1, ss, bx
	    lea     bx, szOEM1
	    cCall   AnsiToOem, <lpSource, lpszOEM1>

	    RegPtr  lpszOEM2, ss, bx
	    lea     bx, szOEM2
	    cCall   AnsiToOem, <lpDest, lpszOEM2>

	    lea     dx, szOEM1
	    lea     di, szOEM2
	    push    ss
	    pop     es
	    mov     ah,RenameFile
	    call    DOS3CALL
	    jc	    DRExit
	    xor     ax,ax	    ; Return 0 if successful
DRExit:
cEnd

;   lmemmove() -
;
;   Shamelessly heisted from the C5.1 runtime library, and modified to
;   work in mixed model Windows programs!
;
;***
;memcpy.asm - contains memcpy and memmove routines
;
;	Copyright (c) 1986-1988, Microsoft Corporation.  All right reserved.
;
;Purpose:
;	memmove() copies a source memory buffer to a destination memory buffer.
;	This routine recognize overlapping buffers to avoid propogation.
;
;   Algorithm:
;
;	void * memmove(void * dst, void * src, size_t count)
;	{
;		void * ret = dst;
;
;		if (dst <= src || dst >= (src + count)) {
;			/*
;			 * Non-Overlapping Buffers
;			 * copy from lower addresses to higher addresses
;			 */
;			while (count--)
;				*dst++ = *src++;
;			}
;		else {
;			/*
;			 * Overlapping Buffers
;			 * copy from higher addresses to lower addresses
;			 */
;			dst += count - 1;
;			src += count - 1;
;
;			while (count--)
;				*dst-- = *src--;
;			}
;
;		return(ret);
;	}
;
;
;Entry:
;	void *dst = pointer to destination buffer
;	const void *src = pointer to source buffer
;	size_t count = number of bytes to copy
;
;Exit:
;	Returns a pointer to the destination buffer in DX:AX
;
;Uses:
;	CX,DX,ES
;
;Exceptions:
;*******************************************************************************

cProc	lmemmove,<FAR,PUBLIC>,<si,di>

	parmD	dst		; destination pointer
	parmD	src		; source pointer
	parmW	count		; number of bytes to copy

cBegin
	push	ds		; Preserve DS
	lds	si,src		; DS:SI = src
	les	di,dst		; ES:DI = dst

	mov	ax,di		; save dst in AX for return value
	mov	cx,count	; cx = number of bytes to move
	jcxz	done		; if cx = 0 Then nothing to copy

;
; Check for overlapping buffers:
;	If segments are different, assume no overlap
;		Do normal (Upwards) Copy
;	Else If (dst <= src) Or (dst >= src + Count) Then
;		Do normal (Upwards) Copy
;	Else
;		Do Downwards Copy to avoid propogation
;
	mov	ax,es		; compare the segments
	cmp	ax,word ptr (src+2)
	jne	CopyUp
	cmp	di,si		; src <= dst ?
	jbe	CopyUp

	mov	ax,si
	add	ax,cx
	cmp	di,ax		; dst >= (src + count) ?
	jae	CopyUp
;
; Copy Down to avoid propogation in overlapping buffers
;
	mov	ax,di		; AX = return value (offset part)

	add	si,cx
	add	di,cx
	dec	si		; DS:SI = src + count - 1
	dec	di		; ES:DI = dst + count - 1
	std			; Set Direction Flag = Down
	rep	movsb
	cld			; Set Direction Flag = Up
	jmp	short done

CopyUp:
	mov	ax,di		; AX = return value (offset part)
;
; There are 4 situations as far as word alignment of "src" and "dst":
;	1. src and dst are both even	(best case)
;	2. src is even and dst is odd
;	3. src is odd and dst is even
;	4. src and dst are both odd	(worst case)
;
; Case #4 is much faster if a single byte is copied before the
; REP MOVSW instruction.  Cases #2 and #3 are effectively unaffected
; by such an operation.  To maximum the speed of this operation,
; only DST is checked for alignment.  For cases #2 and #4, the first
; byte will be copied before the REP MOVSW.
;
	test	al,1		; fast check for dst being odd address
	jz	move

	movsb			; move a byte to improve alignment
	dec	cx
;
; Now the bulk of the copy is done using REP MOVSW.  This is much
; faster than a REP MOVSB if the src and dst addresses are both
; word aligned and the processor has a 16-bit bus.  Depending on
; the initial alignment and the size of the region moved, there
; may be an extra byte left over to be moved.  This is handled
; by the REP MOVSB, which moves either 0 or 1 bytes.
;
move:
	shr	cx,1		; Shift CX for count of words
	rep	movsw		; CF set if one byte left over
	adc	cx,cx		; CX = 1 or 0, depending on Carry Flag
	rep	movsb		; possible final byte
;
; Return the "dst" address in AX/DX:AX
;
done:
	pop	ds		;restore ds
	mov	dx,es		;segment part of dest address

cEnd

sEnd %SEGNAME

end
