542 lines
12 KiB
NASM
542 lines
12 KiB
NASM
;****************************************************************************
|
|
;* *
|
|
;* 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
|
|
|
|
mov al,byte ptr [di+2] ; string just a drive letter and colon?
|
|
cbw
|
|
or ax,ax
|
|
jz SCDExit ; Yup, just set the current drive and done
|
|
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
|