Category : Files from Magazines
Archive   : DDJ0489.ZIP
Filename : EXECSW.ASC
program TestExecSwap;
uses
Dos,ExecSwap; {Keep ExecSwap last}
const
SwapLoc : array[Boolean] of String[7] = ('on disk', 'in EMS');
var
Status : Word;
begin
if not InitExecSwap(HeapPtr, 'SWAP.$$$') then
WriteLn('Unable to allocate swap space')
else begin
WriteLn('Allocated ', BytesSwapped, ' bytes ', SwapLoc[EmsAllocated]);
SwapVectors;
Status := ExecWithSwap(GetEnv('COMSPEC'), '');
SwapVectors;
WriteLn('Execu status; ', Status);
end;
end.
[LISTING TWO]
{Copyright (c) 1988 TurboPower Software}
{May be used freely as long as due credit is given}
{$R-,S-}
unit ExecSwap;
{-Memory-efficient DOS EXEC call}
interface
const
BytesSwapped : LongInt = 0; {Bytes to swap to EMS/disk}
EmsAllocated : Boolean = False; {True when EMS allocated for swap}
FileAllocated : Boolean = False; {True when file allocated for swap}
function ExecWithSwap(Path, CmdLine : String) : Word;
{-DOS EXEC supporting swap to EMS or disk}
function InitExecSwap(LastToSave : Pointer; SwapFileName : String) : Boolean;
{-Initialize for swapping, returning TRUE if successful}
procedure ShutdownExecSwap;
{-Deallocate swap area}
implementation
var
EmsHandle : Word; {Handle of EMS allocation block}
FrameSeg : Word; {Segment of EMS page frame}
FileHandle : Word; {DOS handle of swap file}
SwapName : String[80]; {ASCIIZ name of swap file}
SaveExit : Pointer; {Exit chain pointer}
{$L EXECSWAP}
function ExecWithSwap(Path, CmdLine : String) : Word; external;
procedure FirstToSave; external;
function AllocateSwapFile : Boolean; external;
procedure DeallocateSwapFile; external;
{$F+} {These routines could be interfaced for general use}
function EmsInstalled : Boolean; external;
function EmsPageFrame : Word; external;
function AllocateEmsPages(NumPages : Word) : Word; external;
procedure DeallocateEmsHandle(Handle : Word); external;
function DefaultDrive : Char; external;
function DiskFree(Drive : Byte) : LongInt; external;
procedure ExecSwapExit;
begin
ExitProc := SaveExit;
ShutdownExecSwap;
end;
{$F-}
procedure ShutdownExecSwap;
begin
if EmsAllocated then begin
DeallocateEmsHandle(EmsHandle);
EmsAllocated := False;
end else if FileAllocated then begin
DeallocateSwapFile;
FileAllocated := False;
end;
end;
function PtrDiff(H, L : Pointer) : LongInt;
type
OS = record O, S : Word; end; {Convenient typecast}
begin
PtrDiff := (LongInt(OS(H).S) shl 4+OS(H).O)-
(LongInt(OS(L).S) shl 4+OS(L).O);
end;
function InitExecSwap(LastToSave : Pointer;
SwapFileName : String) : Boolean;
const
EmsPageSize = 16384; {Bytes in a standard EMS page}
var
PagesInEms : Word; {Pages needed in EMS}
BytesFree : LongInt; {Bytes free on swap file drive}
DriveChar : Char; {Drive letter for swap file}
begin
InitExecSwap := False;
if EmsAllocated or FileAllocated then
Exit;
BytesSwapped := PtrDiff(LastToSave, @FirstToSave);
if BytesSwapped <= 0 then
Exit;
SaveExit := ExitProc;
ExitProc := @ExecSwapExit;
if EmsInstalled then begin
PagesInEms := (BytesSwapped+EmsPageSize-1) div EmsPageSize;
EmsHandle := AllocateEmsPages(PagesInEms);
if EmsHandle <> $FFFF then begin
EmsAllocated := True;
FrameSeg := EmsPageFrame;
if FrameSeg <> 0 then begin
InitExecSwap := True;
Exit;
end;
end;
end;
if Length(SwapFileName) <> 0 then begin
SwapName := SwapFileName+#0;
if Pos(':', SwapFileName) = 2 then
DriveChar := Upcase(SwapFileName[1])
else
DriveChar := DefaultDrive;
BytesFree := DiskFree(Byte(DriveChar)-$40);
FileAllocated := (BytesFree > BytesSwapped) and AllocateSwapFile;
if FileAllocated then
InitExecSwap := True;
end;
end;
end.
[LISTING THREE]
;EXECSWAP.ASM
; Swap memory and exec another program
; Copyright (c) 1988 TurboPower Software
; May be used freely as long as due credit is given
;-----------------------------------------------------------------------------
DATA SEGMENT BYTE PUBLIC
EXTRN BytesSwapped:DWORD ;Bytes to swap to EMS/disk
EXTRN EmsAllocated:BYTE ;True when EMS allocated for swap
EXTRN FileAllocated:BYTE ;True when file allocated for swap
EXTRN EmsHandle:WORD ;Handle of EMS allocation block
EXTRN FrameSeg:WORD ;Segment of EMS page frame
EXTRN FileHandle:WORD ;Handle of DOS swap file
EXTRN SwapName:BYTE ;ASCIIZ name of swap file
EXTRN PrefixSeg:WORD ;Base segment of program
DATA ENDS
;-----------------------------------------------------------------------------
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE,DS:DATA
PUBLIC ExecWithSwap,FirstToSave
PUBLIC AllocateSwapFile,DeallocateSwapFile
PUBLIC DefaultDrive,DiskFree
PUBLIC EmsInstalled,EmsPageFrame
PUBLIC AllocateEmsPages,DeallocateEmsHandle
;-----------------------------------------------------------------------------
FileAttr EQU 6 ;Swap file attribute (hidden+system)
EmsPageSize EQU 16384 ;Size of EMS page
FileBlockSize EQU 32768 ;Size of a file block
StkSize EQU 128 ;Bytes in temporary stack
lo EQU (WORD PTR 0) ;Convenient typecasts
hi EQU (WORD PTR 2)
ofst EQU (WORD PTR 0)
segm EQU (WORD PTR 2)
;-----------------------------------------------------------------------------
;Variables in CS
EmsDevice DB 'EMMXXXX0',0 ;Name of EMS device driver
UsedEms DB 0 ;1 if swapping to EMS, 0 if to file
BytesSwappedCS DD 0 ;Bytes to move during a swap
EmsHandleCS DW 0 ;EMS handle
FrameSegCS DW 0 ;Segment of EMS page window
FileHandleCS DW 0 ;DOS file handle
PrefixSegCS DW 0 ;Segment of base of program
Status DW 0 ;ExecSwap status code
LeftToSwap DD 0 ;Bytes left to move
SaveSP DW 0 ;Original stack pointer
SaveSS DW 0 ;Original stack segment
PathPtr DD 0 ;Pointer to program to execute
CmdPtr DD 0 ;Pointer to command line to execute
ParasWeHave DW 0 ;Paragraphs allocated to process
CmdLine DB 128 DUP(0) ;Terminated command line passed to DOS
Path DB 64 DUP(0) ;Terminated path name passed to DOS
FileBlock1 DB 16 DUP(0) ;FCB passed to DOS
FileBlock2 DB 16 DUP(0) ;FCB passed to DOS
EnvironSeg DW 0 ;Segment of environment for child
CmdLinePtr DD 0 ;Pointer to terminated command line
FilePtr1 DD 0 ;Pointer to FCB file
FilePtr2 DD 0 ;Pointer to FCB file
TempStack DB StkSize DUP(0) ;Temporary stack
StackTop LABEL WORD ;Initial top of stack
;-----------------------------------------------------------------------------
;Macros
MovSeg MACRO Dest,Src ;Set one segment register to another
PUSH Src
POP Dest
ENDM
MovMem MACRO Dest,Src ;Move from memory to memory via AX
MOV AX,Src
MOV Dest,AX
ENDM
InitSwapCount MACRO ;Initialize counter for bytes to swap
MovMem LeftToSwap.lo,BytesSwappedCS.lo
MovMem LeftToSwap.hi,BytesSwappedCS.hi
ENDM
SetSwapCount MACRO BlkSize ;Return CX = bytes to move this block
LOCAL FullBlk ;...and reduce total bytes left to move
MOV CX,BlkSize ;Assume we'll write a full block
CMP LeftToSwap.hi,0 ;Is high word still non-zero?
JNZ FullBlk ;Jump if so
CMP LeftToSwap.lo,BlkSize ;Low word still a block or more?
JAE FullBlk ;Jump if so
MOV CX,LeftToSwap.lo ;Otherwise, move what's left
FullBlk:SUB LeftToSwap.lo,CX ;Reduce number left to move
SBB LeftToSwap.hi,0
ENDM
NextBlock MACRO SegReg, BlkSize ;Point SegReg to next block to move
MOV AX,SegReg
ADD AX,BlkSize/16 ;Add paragraphs to next segment
MOV SegReg,AX ;Next block to move
MOV AX,LeftToSwap.lo
OR AX,LeftToSwap.hi ;Bytes left to move?
ENDM
EmsCall MACRO FuncAH ;Call EMM and prepare to check result
MOV AH,FuncAH ;Set up function
INT 67h
OR AH,AH ;Error code in AH
ENDM
DosCallAH MACRO FuncAH ;Call DOS subfunction AH
MOV AH,FuncAH
INT 21h
ENDM
DosCallAX MACRO FuncAX ;Call DOS subfunction AX
MOV AX,FuncAX
INT 21h
ENDM
InitSwapFile MACRO
MOV BX,FileHandleCS ;BX = handle of swap file
XOR CX,CX
XOR DX,DX ;Start of file
DosCallAX 4200h ;DOS file seek
ENDM
HaltWithError MACRO Level ;Halt if non-recoverable error occurs
MOV AL,Level ;Set errorlevel
DosCallAH 4Ch
ENDM
MoveFast MACRO ;Move CX bytes from DS:SI to ES:DI
CLD ;Forward
RCR CX,1 ;Convert to words
REP MOVSW ;Move the words
RCL CX,1 ;Get the odd byte, if any
REP MOVSB ;Move it
ENDM
SetTempStack MACRO ;Switch to temporary stack
MOV AX,OFFSET StackTop ;Point to top of stack
MOV BX,CS ;Temporary stack in this code segment
CLI ;Interrupts off
MOV SS,BX ;Change stack
MOV SP,AX
STI ;Interrupts on
ENDM
;-----------------------------------------------------------------------------
;function ExecWithSwap(Path, CmdLine : string) : Word;
ExecWithSwap PROC FAR
PUSH BP
MOV BP,SP ;Set up stack frame
;Move variables to CS where we can easily access them later
MOV Status,1 ;Assume failure
LES DI,[BP+6] ;ES:DI -> CmdLine
MOV CmdPtr.ofst,DI
MOV CmdPtr.segm,ES ;CmdPtr -> command line string
LES DI,[BP+10] ;ES:DI -> Path
MOV PathPtr.ofst,DI
MOV PathPtr.segm,ES ;PathPtr -> path to execute
MOV SaveSP,SP ;Save stack position
MOV SaveSS,SS
MovMem BytesSwappedCS.lo,BytesSwapped.lo
MovMem BytesSwappedCS.hi,BytesSwapped.hi
MovMem EmsHandleCS,EmsHandle
MovMem FrameSegCS,FrameSeg
MovMem FileHandleCS,FileHandle
MovMem PrefixSegCS,PrefixSeg
InitSwapCount ;Initialize bytes LeftToSwap
;Check for swapping to EMS or file
CMP EmsAllocated,0 ;Check flag for EMS method
JZ NotEms ;Jump if EMS not used
JMP WriteE ;Swap to EMS
NotEms: CMP FileAllocated,0 ;Check flag for swap file method
JNZ WriteF ;Swap to file
JMP ESDone ;Exit if no swapping method set
;Write to swap file
WriteF: MovSeg DS,CS ;DS = CS
InitSwapFile ;Seek to start of swap file
JNC EF0 ;Jump if success
JMP ESDone ;Exit if error
EF0: SetSwapCount FileBlockSize ;CX = bytes to write
MOV DX,OFFSET FirstToSave ;DS:DX -> start of region to save
DosCallAH 40h ;File write
JC EF1 ;Jump if write error
CMP AX,CX ;All bytes written?
JZ EF2 ;Jump if so
EF1: JMP ESDone ;Exit if error
EF2: NextBlock DS,FileBlockSize ;Point DS to next block to write
JNZ EF0 ;Loop if bytes left to write
MOV UsedEms,0 ;Flag we used swap file for swapping
JMP SwapDone ;Done swapping out
;Write to EMS
WriteE: MOV ES,FrameSeg ;ES -> page window
MOV DX,EmsHandle ;DX = handle of our EMS block
XOR BX,BX ;BX = initial logical page
MovSeg DS,CS ;DS = CS
EE0: XOR AL,AL ;Physical page 0
EmsCall 44h ;Map physical page
JZ EE1 ;Jump if success
JMP ESDone ;Exit if error
EE1: SetSwapCount EmsPageSize ;CX = Bytes to move
XOR DI,DI ;ES:DI -> base of EMS page
MOV SI,OFFSET FirstToSave ;DS:SI -> region to save
MoveFast ;Move CX bytes from DS:SI to ES:DI
INC BX ;Next logical page
NextBlock DS,EmsPageSize ;Point DS to next page to move
JNZ EE0 ;Loop if bytes left to move
MOV UsedEms,1 ;Flag we used EMS for swapping
;Shrink memory allocated to this process
SwapDone:MOV AX,PrefixSegCS
MOV ES,AX ;ES = segment of our memory block
DEC AX
MOV DS,AX ;DS = segment of memory control block
MOV CX,DS:[0003h] ;CX = current paragraphs owned
MOV ParasWeHave,CX ;Save current paragraphs owned
SetTempStack ;Switch to temporary stack
MOV AX,OFFSET FirstToSave+15
MOV CL,4
SHR AX,CL ;Convert offset to paragraphs
ADD BX,AX
SUB BX,PrefixSegCS ;BX = new paragraphs to keep
DosCallAH 4Ah ;SetBlock
JNC EX0 ;Jump if successful
JMP EX5 ;Swap back and exit
;Set up parameters and call DOS Exec
EX0: MOV AX,ES:[002Ch] ;Get environment segment
MOV EnvironSeg,AX
MovSeg ES,CS ;ES = CS
LDS SI,PathPtr ;DS:SI -> path to execute
MOV DI,OFFSET Path ;ES:DI -> local ASCIIZ copy
CLD
LODSB ;Read current length
CMP AL,63 ;Truncate if exceeds space set aside
JB EX1
MOV AL,63
EX1: MOV CL,AL
XOR CH,CH ;CX = bytes to copy
REP MOVSB
XOR AL,AL
STOSB ;ASCIIZ terminate
LDS SI,CmdPtr ;DS:SI -> Command line to pass
MOV DI,OFFSET CmdLine ;ES:DI -> Local terminated copy
LODSB
CMP AL,126 ;Truncate command if exceeds space
JB EX2
MOV AL,126
EX2: STOSB
MOV CL,AL
XOR CH,CH ;CX = bytes to copy
REP MOVSB
MOV AL,0DH ;Terminate with ^M
STOSB
MovSeg DS,CS ;DS = CS
MOV SI,OFFSET CmdLine
MOV CmdLinePtr.ofst,SI
MOV CmdLinePtr.segm,DS ;Store pointer to command line
INC SI
MOV DI,OFFSET FileBlock1
MOV FilePtr1.ofst,DI
MOV FilePtr1.segm,ES ;Store pointer to filename 1, if any
DosCallAX 2901h ;Parse FCB
MOV DI,OFFSET FileBlock2
MOV FilePtr2.ofst,DI
MOV FilePtr2.segm,ES ;Store pointer to filename 2, if any
DosCallAX 2901h ;Parse FCB
MOV DX,OFFSET Path
MOV BX,OFFSET EnvironSeg
DosCallAX 4B00h ;Exec
JC EX3 ;Jump if error in DOS call
XOR AX,AX ;Return zero for success
EX3: MOV Status,AX ;Save DOS error code
;Set up temporary stack and reallocate original memory block
SetTempStack ;Set up temporary stack
MOV ES,PrefixSegCS
MOV BX,ParasWeHave
DosCallAH 4Ah ;SetBlock
JNC EX4 ;Jump if no error
HaltWithError 0FFh ;Must halt if failure here
EX4: InitSwapCount ;Initialize LeftToSwap
;Check which swap method is in use
EX5: CMP UsedEms,0
JZ ReadF ;Jump to read back from file
JMP ReadE ;Read back from EMS
;Read back from swap file
ReadF: MovSeg DS,CS ;DS = CS
InitSwapFile ;Seek to start of swap file
JNC EF3 ;Jump if we succeeded
HaltWithError 0FEh ;Must halt if failure here
EF3: SetSwapCount FileBlockSize ;CX = bytes to read
MOV DX,OFFSET FirstToSave ;DS:DX -> start of region to restore
DosCallAH 3Fh ;Read file
JNC EF4 ;Jump if no error
HaltWithError 0FEh ;Must halt if failure here
EF4: CMP AX,CX
JZ EF5 ;Jump if full block read
HaltWithError 0FEh ;Must halt if failure here
EF5: NextBlock DS,FileBlockSize ;Point DS to next page to read
JNZ EF3 ;Jump if bytes left to read
JMP ESDone ;We're done
;Copy back from EMS
ReadE: MOV DS,FrameSegCS ;DS -> page window
MOV DX,EmsHandleCS ;DX = handle of our EMS block
XOR BX,BX ;BX = initial logical page
MovSeg ES,CS ;ES = CS
EE3: XOR AL,AL ;Physical page 0
EmsCall 44h ;Map physical page
JZ EE4 ;Jump if success
HaltWithError 0FDh ;Must halt if failure here
EE4: SetSwapCount EmsPageSize ;CX = Bytes to move
XOR SI,SI ;DS:SI -> base of EMS page
MOV DI,OFFSET FirstToSave ;ES:DI -> region to restore
MoveFast ;Move CX bytes from DS:SI to ES:DI
INC BX ;Next logical page
NextBlock ES,EmsPageSize ;Point ES to next page to move
JNZ EE3 ;Jump if so
ESDone: CLI ;Switch back to original stack
MOV SS,SaveSS
MOV SP,SaveSP
STI
MOV AX,SEG DATA
MOV DS,AX ;Restore DS
MOV AX,Status ;Return status
POP BP
RET 8 ;Remove parameters and return
ExecWithSwap ENDP
;-----------------------------------------------------------------------------
;Label marks first location to swap
FirstToSave:
;-----------------------------------------------------------------------------
;function AllocateSwapFile : Boolean;
AllocateSwapFile PROC NEAR
MOV CX,FileAttr ;Attribute for swap file
MOV DX,OFFSET SwapName+1 ;DS:DX -> ASCIIZ swap name
DosCallAH 3Ch ;Create file
MOV FileHandle,AX ;Save handle assuming success
MOV AL,0 ;Assume failure
JC ASDone ;Failed if carry set
INC AL ;Return true for success
ASDone: RET
AllocateSwapFile ENDP
;-----------------------------------------------------------------------------
;procedure DeallocateSwapFile;
DeallocateSwapFile PROC NEAR
MOV BX,FileHandle ;Handle of swap file
DosCallAH 3Eh ;Close file
XOR CX,CX ;Normal attribute
MOV DX,OFFSET SwapName+1 ;DS:DX -> ASCIIZ swap name
DosCallAX 4301h ;Set file attribute
DosCallAH 41h ;Delete file
RET
DeallocateSwapFile ENDP
;-----------------------------------------------------------------------------
;function EmsInstalled : Boolean;
EmsInstalled PROC FAR
PUSH DS
MovSeg DS,CS ;DS = CS
MOV DX,OFFSET EmsDevice ;DS:DX -> EMS driver name
DosCallAX 3D02h ;Open for read/write
POP DS
MOV BX,AX ;Save handle in case one returned
MOV AL,0 ;Assume FALSE
JC EIDone
DosCallAH 3Eh ;Close file
MOV AL,1 ;Return TRUE
EIDone: RET
EmsInstalled ENDP
;-----------------------------------------------------------------------------
;function EmsPageFrame : Word;
EmsPageFrame PROC FAR
EmsCall 41h ;Get page frame
MOV AX,BX ;AX = segment
JZ EPDone ;Done if Error = 0
XOR AX,AX ;Else segment = 0
EPDone: RET
EmsPageFrame ENDP
;-----------------------------------------------------------------------------
;function AllocateEmsPages(NumPages : Word) : Word;
AllocateEmsPages PROC FAR
MOV BX,SP ;Set up stack frame
MOV BX,SS:[BX+4] ;BX = NumPages
EmsCall 43h ;Allocate EMS
MOV AX,DX ;Assume success
JZ APDone ;Done if not 0
MOV AX,0FFFFh ;$FFFF for failure
APDone: RET 2 ;Remove parameter and return
AllocateEmsPages ENDP
;-----------------------------------------------------------------------------
;procedure DeallocateEmsHandle(Handle : Word);
DeallocateEmsHandle PROC FAR
MOV BX,SP ;Set up stack frame
MOV DX,SS:[BX+4] ;DX = Handle
EmsCall 45h ;Deallocate EMS
RET 2 ;Remove parameter and return
DeallocateEmsHandle ENDP
;-----------------------------------------------------------------------------
;function DefaultDrive : Char;
DefaultDrive PROC FAR
DosCallAH 19h ;Get default drive
ADD AL,'A' ;Convert to character
RET
DefaultDrive ENDP
;-----------------------------------------------------------------------------
;function DiskFree(Drive : Byte) : LongInt;
DiskFree PROC FAR
MOV BX,SP ;Set up stack frame
MOV DL,SS:[BX+4] ;DL = Drive to check
DosCallAH 36h ;Get disk space
MOV DX,AX ;Return 0FFFFFFFFh for failure
CMP AX,0FFFFh ;Bad drive number?
JZ DFDone ;Jump if so
MUL CX ;AX = bytes/cluster
MUL BX ;DX:AX = bytes free
DFDone: RET 2 ;Remove parameter and return
DiskFree ENDP
;-----------------------------------------------------------------------------
CODE ENDS
END
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/