You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1679 lines
53 KiB
1679 lines
53 KiB
/* SCCSID = %W% %E% */
|
|
/*
|
|
* Copyright Microsoft Corporation, 1983-1987
|
|
*
|
|
* This Module contains Proprietary Information of Microsoft
|
|
* Corporation and should be treated as Confidential.
|
|
*/
|
|
/****************************************************************
|
|
* *
|
|
* NEWUTL.C *
|
|
* *
|
|
* Linker utilities. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include <minlit.h> /* Types, constants */
|
|
#include <bndtrn.h> /* More types and constants */
|
|
#include <bndrel.h> /* More types and constants */
|
|
#include <lnkio.h> /* Linker I/O definitions */
|
|
#include <lnkmsg.h> /* Error messages */
|
|
#include <newdeb.h> /* CodeView support */
|
|
#include <extern.h> /* External declarations */
|
|
#include <nmsg.h> /* Near message strings */
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#if EXE386
|
|
#include <exe386.h>
|
|
#endif
|
|
#if NEWIO
|
|
#include <errno.h> /* System error codes */
|
|
#endif
|
|
#if USE_REAL
|
|
#if NOT defined( _WIN32 )
|
|
#define i386
|
|
#include <windows.h>
|
|
#endif
|
|
// The memory sizes are in paragraphs.
|
|
#define TOTAL_CONV_MEM (0xFFFF)
|
|
#define CONV_MEM_FOR_TNT (0x800) // 32K of memory
|
|
#define MIN_CONV_MEM (0x1900) // 100 K of memory
|
|
|
|
typedef unsigned short selector_t ; //Define type to hold selectors
|
|
|
|
static selector_t convMemSelector ; // Selector to conv memory.
|
|
static short noOfParagraphs ; // size of the available blocks in paragraphs
|
|
static int realModeMemPageable ; // = FALSE
|
|
#endif
|
|
|
|
#if WIN_NT OR DOSX32
|
|
unsigned char FCHGDSK(int drive)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
#define DISPLAY_ON FALSE
|
|
#if DISPLAY_ON
|
|
extern int TurnDisplayOn;
|
|
#endif
|
|
APROPCOMDATPTR comdatPrev=NULL; /* Pointer to symbol table entry */
|
|
int fSameComdat=FALSE; /* Set if LINSYM to the same COMDAT */
|
|
|
|
/********************************************************************
|
|
* INPUT ROUTINES *
|
|
********************************************************************/
|
|
|
|
|
|
/*** GetLineOff - read part of LINNUM record
|
|
*
|
|
* Purpose:
|
|
* This function reads line/offset pair from LINNUM record. It is here
|
|
* because we want to keep all the I/O functions near and the LINNUM
|
|
* processing is performed in NEWDEB.C which resides in another segment.
|
|
*
|
|
* Input:
|
|
* - pLine - pointer to line number
|
|
* - pRa - pointer to offset
|
|
*
|
|
* Output:
|
|
* Returns line/offset pair from OMF record.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void GetLineOff(WORD *pLine, RATYPE *pRa)
|
|
{
|
|
*pLine = WGets() + QCLinNumDelta; // Get line number
|
|
|
|
// Get code segment offset
|
|
|
|
#if OMF386
|
|
if (rect & 1)
|
|
*pRa = LGets();
|
|
else
|
|
#endif
|
|
*pRa = (RATYPE) WGets();
|
|
}
|
|
|
|
/*** GetGsnInfo - read the segment index of the LINNUM
|
|
*
|
|
* Purpose:
|
|
* This function reads the segemnt index from LINNUM record. It is here
|
|
* because we want to keep all the I/O functions near and the LINNUM
|
|
* processing is performed in NEWDEB.C which resides in another segment.
|
|
*
|
|
* Input:
|
|
* - pRa - pointer to offset correction for COMDATs
|
|
*
|
|
* Output:
|
|
* Returns global segment index and for lines in COMDAT record
|
|
* offset correction.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
WORD GetGsnInfo(GSNINFO *pInfo)
|
|
{
|
|
WORD fSuccess; // TRUE if everything is OK
|
|
WORD attr; // COMDAT flags
|
|
WORD comdatIdx; // COMDAT symbol index
|
|
APROPCOMDATPTR comdat; // Pointer to symbol table entry
|
|
|
|
|
|
fSuccess = TRUE;
|
|
if (TYPEOF(rect) == LINNUM)
|
|
{
|
|
// Read regular LINNUM record
|
|
|
|
GetIndex((WORD)0,(WORD)(grMac - 1)); // Skip group index
|
|
pInfo->gsn = mpsngsn[GetIndex((WORD)1,(WORD)(snMac - 1))];
|
|
// Get global SEGDEF number
|
|
pInfo->comdatRa = 0L;
|
|
pInfo->comdatSize = 0L;
|
|
pInfo->fComdat = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Read LINSYM record - line numbers for COMDAT
|
|
|
|
attr = (WORD) Gets();
|
|
comdatIdx = GetIndex(1, (WORD)(lnameMac - 1));
|
|
comdat = (APROPCOMDATPTR ) PropRhteLookup(mplnamerhte[comdatIdx], ATTRCOMDAT, FALSE);
|
|
fSameComdat = FALSE;
|
|
if (comdat != NULL)
|
|
{
|
|
if(comdat == comdatPrev)
|
|
fSameComdat = 1;
|
|
else
|
|
comdatPrev = comdat;
|
|
|
|
if ((fPackFunctions && !(comdat->ac_flags & REFERENCED_BIT)) ||
|
|
!(comdat->ac_flags & SELECTED_BIT) ||
|
|
comdat->ac_obj != vrpropFile)
|
|
{
|
|
SkipBytes((WORD)(cbRec - 1));
|
|
fSuccess = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pInfo->gsn = comdat->ac_gsn;
|
|
pInfo->comdatRa = comdat->ac_ra;
|
|
pInfo->comdatSize = comdat->ac_size;
|
|
pInfo->comdatAlign= comdat->ac_align;
|
|
pInfo->fComdat = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SkipBytes((WORD)(cbRec - 1));
|
|
fSuccess = FALSE;
|
|
}
|
|
}
|
|
return(fSuccess);
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* Gets: *
|
|
* *
|
|
* Read a byte of input and return it. *
|
|
* *
|
|
****************************************************************/
|
|
#if NOASM
|
|
#if !defined( M_I386 ) && !defined( _WIN32 )
|
|
WORD NEAR Gets(void)
|
|
{
|
|
REGISTER WORD b;
|
|
|
|
if((b = getc(bsInput)) == EOF) InvalidObject();
|
|
/* After reading the byte, decrement the OMF record counter. */
|
|
--cbRec;
|
|
return(b);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if ALIGN_REC
|
|
#else
|
|
/****************************************************************
|
|
* *
|
|
* WGetsHard: *
|
|
* *
|
|
* Read a word of input and return it. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
WORD NEAR WGetsHard()
|
|
{
|
|
REGISTER WORD w;
|
|
|
|
// handle hard case... easy case already tested in WGets
|
|
|
|
w = Gets(); /* Get low-order byte */
|
|
return(w | (Gets() << BYTELN)); /* Return word */
|
|
}
|
|
|
|
#if OMF386
|
|
/****************************************************************
|
|
* *
|
|
* LGets: *
|
|
* *
|
|
* Read a long word of input and return it. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
DWORD NEAR LGets()
|
|
{
|
|
DWORD lw;
|
|
FILE * f = bsInput;
|
|
|
|
// NOTE: this code will only work on a BigEndian machine
|
|
if (f->_cnt >= sizeof(DWORD))
|
|
{
|
|
lw = *(DWORD *)(f->_ptr);
|
|
f->_ptr += sizeof(DWORD);
|
|
f->_cnt -= sizeof(DWORD);
|
|
cbRec -= sizeof(DWORD);
|
|
return lw;
|
|
}
|
|
|
|
lw = WGets(); /* Get low-order word */
|
|
return(lw | ((DWORD) WGets() << 16));/* Return long word */
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if 0
|
|
/****************************************************************
|
|
* *
|
|
* GetBytes: *
|
|
* *
|
|
* Read n bytes from input. *
|
|
* If n is greater than SBLEN - 1, issue a fatal error. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR GetBytes(pb,n)
|
|
BYTE *pb; /* Pointer to buffer */
|
|
WORD n; /* Number of bytes to read in */
|
|
{
|
|
FILE *f = bsInput;
|
|
|
|
if(n >= SBLEN)
|
|
InvalidObject();
|
|
|
|
if (n <= f->_cnt)
|
|
{
|
|
memcpy(pb,f->_ptr, n);
|
|
f->_cnt -= n;
|
|
f->_ptr += n;
|
|
}
|
|
else
|
|
fread(pb,1,n,f); /* Ask for n bytes */
|
|
|
|
cbRec -= n; /* Update byte count */
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
/****************************************************************
|
|
* *
|
|
* SkipBytes: *
|
|
* *
|
|
* Skip n bytes of input. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR SkipBytes(n)
|
|
REGISTER WORD n; /* Number of bytes to skip */
|
|
{
|
|
#if WIN_NT
|
|
WORD cbRead;
|
|
SBTYPE skipBuf;
|
|
|
|
cbRec -= n; // Update byte count
|
|
while (n) // While there are bytes to skip
|
|
{
|
|
cbRead = n < sizeof(SBTYPE) ? n : sizeof(SBTYPE);
|
|
if (fread(skipBuf, 1, cbRead, bsInput) != cbRead)
|
|
InvalidObject();
|
|
n -= cbRead;
|
|
}
|
|
#else
|
|
FILE *f = bsInput;
|
|
|
|
if (f->_cnt >= n)
|
|
{
|
|
f->_cnt -= n;
|
|
f->_ptr += n;
|
|
}
|
|
else if(fseek(f,(long) n,1))
|
|
InvalidObject();
|
|
cbRec -= n; /* Update byte count */
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************
|
|
* *
|
|
* GetIndexHard: (GetIndex -- hard case) *
|
|
* *
|
|
* This function reads in a variable-length index field from *
|
|
* the input file. It takes as its arguments two word values *
|
|
* which represent the minimum and maximum allowable values *
|
|
* the index. The function returns the value of the index. *
|
|
* See p. 12 in "8086 Object Module Formats EPS." *
|
|
* *
|
|
****************************************************************/
|
|
|
|
WORD NEAR GetIndexHard(imin,imax)
|
|
WORD imin; /* Minimum permissible value */
|
|
WORD imax; /* Maximum permissible value */
|
|
{
|
|
REGISTER WORD index;
|
|
|
|
FILE *f = bsInput;
|
|
|
|
if (f->_cnt >= sizeof(WORD))
|
|
{
|
|
index = *(BYTE *)(f->_ptr);
|
|
if (index & 0x80)
|
|
{
|
|
index <<= BYTELN;
|
|
index |= *(BYTE *)(f->_ptr+1);
|
|
index &= 0x7fff;
|
|
f->_cnt -= sizeof(WORD);
|
|
f->_ptr += sizeof(WORD);
|
|
cbRec -= sizeof(WORD);
|
|
}
|
|
else
|
|
{
|
|
f->_cnt--;
|
|
f->_ptr++;
|
|
cbRec--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((index = Gets()) & 0x80)
|
|
index = ((index & 0x7f) << BYTELN) | Gets();
|
|
}
|
|
|
|
if(index < imin || index > imax) InvalidObject();
|
|
return(index); /* Return a good value */
|
|
}
|
|
|
|
/********************************************************************
|
|
* STRING ROUTINES *
|
|
********************************************************************/
|
|
|
|
#if OSEGEXE
|
|
#if NOASM
|
|
/****************************************************************
|
|
* *
|
|
* zcheck: *
|
|
* *
|
|
* Determine length of initial nonzero stream in a buffer, and *
|
|
* return the length. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#if defined(M_I386)
|
|
#pragma auto_inline(off)
|
|
#endif
|
|
|
|
WORD zcheck(BYTE *pb, WORD cb)
|
|
{
|
|
// Loop down from end until a nonzero byte found.
|
|
// Return length of remainder of buffer.
|
|
|
|
#if defined(M_I386)
|
|
|
|
_asm
|
|
{
|
|
push edi ; Save edi
|
|
movzx ecx, cb ; Number of bytes to check
|
|
push ds ; Copy ds into es
|
|
pop es
|
|
xor eax, eax ; Looking for zeros
|
|
mov edi, pb ; Start of buffer
|
|
add edi, ecx ; Just past the end of buffer
|
|
dec edi ; Last byte in the buffer
|
|
std ; Decrement pointer
|
|
repz scasb ; Scan until non-zero byte found
|
|
jz AllZeros ; Buffer truly empty
|
|
inc ecx ; Fix count
|
|
|
|
AllZeros:
|
|
cld ; Clear flag just to be safe
|
|
pop edi ; Restore edi
|
|
mov eax, ecx ; Return count in eax
|
|
}
|
|
#endif
|
|
|
|
for(pb = &pb[cb]; cb != 0; --cb)
|
|
if(*--pb != '\0') break;
|
|
return(cb);
|
|
}
|
|
#endif
|
|
#endif /* OSEGEXE */
|
|
|
|
#if defined(M_I386)
|
|
#pragma auto_inline(on)
|
|
#endif
|
|
|
|
/*** CheckSegmentsMemory - check is all segments have allocated memory
|
|
*
|
|
* Purpose:
|
|
* Check for not initialized segments. If the segment have a non-zero
|
|
* size but no initialized data, then we have to allocate for it a
|
|
* zero filled memory buffer. Normally 'MoveToVm' allocates memory
|
|
* buffer for segments, but in this case there was no 'moves to VM'.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void CheckSegmentsMemory(void)
|
|
{
|
|
SEGTYPE seg;
|
|
SATYPE sa;
|
|
|
|
if (fNewExe)
|
|
{
|
|
for (sa = 1; sa < saMac; sa++)
|
|
if (mpsaMem[sa] == NULL && mpsacb[sa] > 0)
|
|
mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]);
|
|
}
|
|
else
|
|
{
|
|
for (seg = 1; seg <= segLast; seg++)
|
|
if (mpsegMem[seg] == NULL && mpsegcb[seg] > 0)
|
|
mpsegMem[seg] = (BYTE FAR *) GetMem(mpsegcb[seg] + mpsegraFirst[seg]);
|
|
}
|
|
}
|
|
|
|
/*** WriteExe - write bytes to the executable file
|
|
*
|
|
* Purpose:
|
|
* Write to the executable file and check for errors.
|
|
*
|
|
* Input:
|
|
* pb - byte buffer to write
|
|
* cb - buffer size in bytes
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
*
|
|
* Exceptions:
|
|
* I/O problems - fatal error and abort
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#if !defined( M_I386 ) && !defined( _WIN32 )
|
|
|
|
#pragma check_stack(on)
|
|
|
|
void WriteExe(void FAR *pb, unsigned cb)
|
|
{
|
|
BYTE localBuf[1024];
|
|
WORD count;
|
|
|
|
while (cb > 0)
|
|
{
|
|
count = (WORD) (cb <= sizeof(localBuf) ? cb : sizeof(localBuf));
|
|
FMEMCPY((BYTE FAR *) localBuf, pb, count);
|
|
if (fwrite((char *) localBuf, sizeof(BYTE), count, bsRunfile) != count)
|
|
{
|
|
ExitCode = 4;
|
|
Fatal(ER_spcrun, strerror(errno));
|
|
}
|
|
cb -= count;
|
|
((BYTE FAR *) pb) += count;
|
|
}
|
|
}
|
|
|
|
#pragma check_stack(off)
|
|
|
|
#else
|
|
|
|
/*** NoRoomForExe - the exe didn't fit
|
|
*
|
|
* Purpose:
|
|
* emit error message
|
|
* give fatal error and abort
|
|
*
|
|
* Input:
|
|
* errno must be set
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void NoRoomForExe()
|
|
{
|
|
ExitCode = 4;
|
|
Fatal(ER_spcrun, strerror(errno));
|
|
}
|
|
|
|
#endif
|
|
|
|
/*** WriteZeros - write zero bytes to the executable file
|
|
*
|
|
* Purpose:
|
|
* Pad executable file with zero bytes.
|
|
*
|
|
* Input:
|
|
* cb - number of bytes to write
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
*
|
|
* Exceptions:
|
|
* I/O problems - fatal error and abort
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void WriteZeros(unsigned cb)
|
|
{
|
|
BYTE buf[512];
|
|
unsigned count;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
while (cb > 0)
|
|
{
|
|
count = cb <= sizeof(buf) ? cb : sizeof(buf);
|
|
WriteExe(buf, count);
|
|
cb -= count;
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* MoveToVm: *
|
|
* *
|
|
* Move a piece of data into a virtual memory area/va. *
|
|
* *
|
|
* Input: cb Count of bytes to be moved. *
|
|
* obData Address of data to be moved. *
|
|
* seg Logical segment to which data belongs. *
|
|
* ra Offset at which data belongs. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#pragma intrinsic(memcpy)
|
|
|
|
#if EXE386
|
|
void MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra)
|
|
#else
|
|
void NEAR MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra)
|
|
#endif
|
|
{
|
|
long cbtot; /* Count of bytes total */
|
|
long cbSeg; /* Segment size */
|
|
WORD fError;
|
|
BYTE FAR *pMemImage;
|
|
CVINFO FAR *pCVInfo;
|
|
SATYPE sa;
|
|
|
|
|
|
cbtot = (long) cb + ra;
|
|
|
|
if (fDebSeg)
|
|
{
|
|
pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo;
|
|
if (pCVInfo)
|
|
{
|
|
if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal))
|
|
{
|
|
cbSeg = pCVInfo->cv_cbTyp;
|
|
pMemImage = pCVInfo->cv_typ;
|
|
}
|
|
else
|
|
{
|
|
cbSeg = pCVInfo->cv_cbSym;
|
|
pMemImage = pCVInfo->cv_sym;
|
|
}
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = cbtot > cbSeg;
|
|
}
|
|
else
|
|
{
|
|
OutError(ER_badcvseg);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fNewExe)
|
|
{
|
|
cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx;
|
|
sa = mpsegsa[seg];
|
|
if (mpsaMem[sa] == NULL)
|
|
mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]);
|
|
pMemImage = mpsaMem[sa];
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg;
|
|
|
|
// If data is going up to or past current end of initialized data,
|
|
// omit any trailing null bytes and reset mpsacbinit. Mpsacbinit
|
|
// will usually go up but may go down if a common segment over-
|
|
// writes previous end data with nulls.
|
|
|
|
if ((DWORD) cbtot >= mpsacbinit[sa])
|
|
{
|
|
if ((DWORD) ra < mpsacbinit[sa] ||
|
|
(cb = zcheck(obData,cb)) != 0)
|
|
mpsacbinit[sa] = (long) ra + cb;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cbSeg = mpsegcb[seg] + mpsegraFirst[seg];
|
|
if (mpsegMem[seg] == NULL)
|
|
mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg);
|
|
pMemImage = mpsegMem[seg];
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = cbtot > cbSeg;
|
|
}
|
|
}
|
|
|
|
if (fError)
|
|
{
|
|
if (!fDebSeg)
|
|
OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch));
|
|
else
|
|
OutError(ER_badcvseg);
|
|
}
|
|
else
|
|
FMEMCPY(&pMemImage[ra], obData, cb);
|
|
}
|
|
|
|
#pragma function(memcpy)
|
|
|
|
#if (OSEGEXE AND ODOS3EXE) OR EXE386
|
|
/*
|
|
* Map segment index to memory image address for new-format exes.
|
|
*/
|
|
BYTE FAR * NEAR msaNew (SEGTYPE seg)
|
|
{
|
|
return(mpsaMem[mpsegsa[seg]]);
|
|
}
|
|
#endif
|
|
|
|
#if (OSEGEXE AND ODOS3EXE) OR EXE386
|
|
/*
|
|
* Map segment index to memory image address for DOS3 or 286Xenix exes.
|
|
*/
|
|
BYTE FAR * NEAR msaOld (SEGTYPE seg)
|
|
{
|
|
return(mpsegMem[seg]);
|
|
}
|
|
#endif
|
|
|
|
#if EXE386
|
|
/*
|
|
* Map segment index to VM area address for 386 exes.
|
|
*/
|
|
long NEAR msa386 (seg)
|
|
SEGTYPE seg;
|
|
{
|
|
register long *p; /* Pointer to mpsegcb */
|
|
register long *pEnd; /* Pointer to end of mpsegcb */
|
|
register long va = AREAFSG; /* Current VM address */
|
|
|
|
/*
|
|
* Segment number-to-VM area mapping is different for 386 segments
|
|
* because their size limit is so big that allocating a fixed amount
|
|
* for each segment is impractical, especially when sdb support is
|
|
* enabled. So segments are allocated contiguously. Each segment
|
|
* is padded to a VM page boundary for efficiency.
|
|
*
|
|
* Implementation: the fastest way would be to allocate a segment
|
|
* based table of virtual addresses, but this would take more code
|
|
* and memory. Counting segment sizes is slower but this is not
|
|
* time-critical routine, and in most cases there will be very few
|
|
* segments.
|
|
*/
|
|
if (fNewExe)
|
|
{
|
|
p = &mpsacb[1];
|
|
pEnd = &mpsacb[seg];
|
|
}
|
|
#if ODOS3EXE
|
|
else
|
|
{
|
|
p = &mpsegcb[1];
|
|
pEnd = &mpsegcb[seg];
|
|
}
|
|
#endif
|
|
for( ; p < pEnd; ++p)
|
|
va += (*p + (PAGLEN - 1)) & ~(PAGLEN - 1);
|
|
return(va);
|
|
}
|
|
#endif /* EXE386 */
|
|
|
|
|
|
|
|
/********************************************************************
|
|
* (ERROR) MESSAGE ROUTINES *
|
|
********************************************************************/
|
|
#pragma auto_inline(off)
|
|
/*
|
|
* SysFatal : system-level error
|
|
*
|
|
* Issue error message and exit with return code 4.
|
|
*/
|
|
void cdecl SysFatal (MSGTYPE msg)
|
|
{
|
|
ExitCode = 4;
|
|
Fatal(msg);
|
|
}
|
|
|
|
|
|
|
|
void NEAR InvalidObject(void)
|
|
{
|
|
Fatal((MSGTYPE)(fDrivePass ? ER_badobj: ER_eofobj));
|
|
}
|
|
|
|
#pragma auto_inline(on)
|
|
/********************************************************************
|
|
* MISCELLANEOUS ROUTINES *
|
|
********************************************************************/
|
|
|
|
/*
|
|
* Output a word integer.
|
|
*/
|
|
void OutWord(x)
|
|
WORD x; /* A word integer */
|
|
{
|
|
WriteExe(&x, CBWORD);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetLocName : read in a symbol name for L*DEF
|
|
*
|
|
* Transform the name by prefixing a space followed by the
|
|
* module number. Update the length byte.
|
|
*
|
|
* Parameters: pointer to a string buffer, 1st byte already
|
|
* contains length
|
|
* Returns: nothing
|
|
*/
|
|
void NEAR GetLocName (psb)
|
|
BYTE *psb; /* Name buffer */
|
|
{
|
|
WORD n;
|
|
BYTE *p;
|
|
|
|
p = &psb[1]; /* Start after length byte */
|
|
*p++ = 0x20; /* Prefix begins with space char */
|
|
GetBytes(p,B2W(psb[0])); /* Read in text of symbol */
|
|
p += B2W(psb[0]); /* Go to end of string */
|
|
*p++ = 0x20;
|
|
n = modkey; /* Initialize */
|
|
/* Convert the module key to ASCII and store backwards */
|
|
do
|
|
{
|
|
*p++ = (BYTE) ((n % 10) + '0');
|
|
n /= 10;
|
|
} while(n);
|
|
psb[0] = (BYTE) ((p - (psb + 1))); /* Update length byte */
|
|
}
|
|
|
|
|
|
|
|
PROPTYPE EnterName(psym,attr,fCreate)
|
|
BYTE *psym; /* Pointer to length-prefixed string */
|
|
ATTRTYPE attr; /* Attribute to look up */
|
|
WORD fCreate; /* Create prop cell if not found */
|
|
{
|
|
return(PropSymLookup(psym, attr, fCreate));
|
|
/* Hide call to near function */
|
|
}
|
|
|
|
#if CMDMSDOS
|
|
|
|
#pragma check_stack(on)
|
|
|
|
/*** ValidateRunFileName - Check if output file has proper extension
|
|
*
|
|
* Purpose:
|
|
* Check user-specified output file name for valid extension.
|
|
* Issue warning if extension is invalid and create new file
|
|
* name with proper extension.
|
|
*
|
|
* Input:
|
|
* ValidExtension - pointer to length prefixed ascii string
|
|
* representing valid exetension for output
|
|
* file name.
|
|
* ForceExtension - TRUE if output file must have new extension,
|
|
* otherwise user responce takes precedence.
|
|
* WarnUser - If TRUE than display L4045 if file name changed.
|
|
*
|
|
* Output:
|
|
* rhteRunfile - global virtual pointer to output file
|
|
* name, changed only if new output name
|
|
* is created because of invalid original
|
|
* extension.
|
|
* warning L4045 - if output file name have to be changed.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
void NEAR ValidateRunFileName(BYTE *ValidExtension,
|
|
WORD ForceExtension,
|
|
WORD WarnUser)
|
|
{
|
|
SBTYPE sb; /* String buffer */
|
|
BYTE *psbRunfile; /* Name of runfile */
|
|
char oldDrive[_MAX_DRIVE];
|
|
char oldDir[_MAX_DIR];
|
|
char oldName[_MAX_FNAME];
|
|
char oldExt[_MAX_EXT];
|
|
|
|
|
|
/* Get the name of the runfile and check if it has user supplied extension */
|
|
|
|
psbRunfile = GetFarSb(((AHTEPTR) FetchSym(rhteRunfile,FALSE))->cch);
|
|
_splitpath(psbRunfile, oldDrive, oldDir, oldName, oldExt);
|
|
|
|
/* Force extension only when no user defined extension */
|
|
|
|
if (ForceExtension && oldExt[0] == NULL)
|
|
{
|
|
memcpy(sb, ValidExtension, strlen(ValidExtension));
|
|
memcpy(bufg, psbRunfile, 1 + B2W(*psbRunfile));
|
|
}
|
|
else
|
|
{
|
|
memcpy(bufg, ValidExtension, strlen(ValidExtension));
|
|
memcpy(sb, psbRunfile, 1 + B2W(*psbRunfile));
|
|
}
|
|
UpdateFileParts(bufg, sb);
|
|
|
|
/* If the name has changed, issue a warning and update rhteRunfile. */
|
|
|
|
if (!SbCompare(bufg, psbRunfile, (FTYPE) TRUE))
|
|
{
|
|
if (WarnUser && !SbCompare(ValidExtension, sbDotExe, (FTYPE) TRUE))
|
|
OutWarn(ER_outputname,bufg + 1);
|
|
PropSymLookup(bufg, ATTRNIL, TRUE);
|
|
rhteRunfile = vrhte;
|
|
}
|
|
}
|
|
|
|
#pragma check_stack(off)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/********************************************************************
|
|
* PORTABILITY ROUTINES *
|
|
********************************************************************/
|
|
|
|
#if M_BYTESWAP
|
|
WORD getword(cp) /* Get a word given a pointer */
|
|
REGISTER char *cp; /* Pointer */
|
|
{
|
|
return(B2W(cp[0]) + (B2W(cp[1]) << BYTELN));
|
|
/* Return 8086-style word */
|
|
}
|
|
|
|
DWORD getdword(cp)/* Get a double word given a pointer */
|
|
REGISTER char *cp; /* Pointer */
|
|
{
|
|
return(getword(cp) + (getword(cp+2) << WORDLN));
|
|
/* Return 8086-style double word */
|
|
}
|
|
#endif
|
|
|
|
#if NOT M_WORDSWAP OR M_BYTESWAP
|
|
/*
|
|
* Portable structure I/O routines
|
|
*/
|
|
#define cget(f) fgetc(f)
|
|
|
|
static int bswap; /* Byte-swapped mode (1 on; 0 off) */
|
|
static int wswap; /* Word-swapped mode (1 on; 0 off) */
|
|
|
|
static cput(c,f)
|
|
char c;
|
|
FILE *f;
|
|
{
|
|
#if FALSE AND OEXE
|
|
CheckSum(1, &c);
|
|
#endif
|
|
fputc(c, f);
|
|
}
|
|
|
|
static pshort(s,f)
|
|
REGISTER short s;
|
|
REGISTER FILE *f;
|
|
{
|
|
cput(s & 0xFF,f); /* Low byte */
|
|
cput(s >> 8,f); /* High byte */
|
|
}
|
|
|
|
static unsigned short gshort(f)
|
|
REGISTER FILE *f;
|
|
{
|
|
REGISTER short s;
|
|
|
|
s = cget(f); /* Get low byte */
|
|
return(s + (cget(f) << 8)); /* Get high byte */
|
|
}
|
|
|
|
static pbshort(s,f)
|
|
REGISTER short s;
|
|
REGISTER FILE *f;
|
|
{
|
|
cput(s >> 8,f); /* High byte */
|
|
cput(s & 0xFF,f); /* Low byte */
|
|
}
|
|
|
|
static unsigned short gbshort(f)
|
|
REGISTER FILE *f;
|
|
{
|
|
REGISTER short s;
|
|
|
|
s = cget(f) << 8; /* Get high byte */
|
|
return(s + cget(f)); /* Get low byte */
|
|
}
|
|
|
|
static int (*fpstab[2])() =
|
|
{
|
|
pshort,
|
|
pbshort
|
|
};
|
|
static unsigned short (*fgstab[2])() =
|
|
{
|
|
gshort,
|
|
gbshort
|
|
};
|
|
|
|
static plong(l,f)
|
|
long l;
|
|
REGISTER FILE *f;
|
|
{
|
|
(*fpstab[bswap])((short)(l >> 16),f);
|
|
/* High word */
|
|
(*fpstab[bswap])((short) l,f); /* Low word */
|
|
}
|
|
|
|
static long glong(f)
|
|
REGISTER FILE *f;
|
|
{
|
|
long l;
|
|
|
|
l = (long) (*fgstab[bswap])(f) << 16;
|
|
/* Get high word */
|
|
return(l + (unsigned) (*fgstab[bswap])(f));
|
|
/* Get low word */
|
|
}
|
|
|
|
static pwlong(l,f)
|
|
long l;
|
|
REGISTER FILE *f;
|
|
{
|
|
(*fpstab[bswap])((short) l,f); /* Low word */
|
|
(*fpstab[bswap])((short)(l >> 16),f);
|
|
/* High word */
|
|
}
|
|
|
|
static long gwlong(f)
|
|
REGISTER FILE *f;
|
|
{
|
|
long l;
|
|
|
|
l = (unsigned) (*fgstab[bswap])(f); /* Get low word */
|
|
return(l + ((long) (*fgstab[bswap])(f) << 16));
|
|
/* Get high word */
|
|
}
|
|
|
|
static int (*fpltab[2])() =
|
|
{
|
|
plong,
|
|
pwlong
|
|
};
|
|
static long (*fgltab[2])() =
|
|
{
|
|
glong,
|
|
gwlong
|
|
};
|
|
|
|
/*
|
|
* int swrite(cp,dopevec,count,file)
|
|
* char *cp;
|
|
* char *dopevec;
|
|
* int count;
|
|
* FILE *file;
|
|
*
|
|
* Returns number of bytes written.
|
|
*
|
|
* Dopevec is a character string with the
|
|
* following format:
|
|
*
|
|
* "[b][w][p]{[<cnt>]<type>}"
|
|
*
|
|
* where [...] denotes an optional part, {...} denotes a part
|
|
* that may be repeated zero or more times, and <...> denotes
|
|
* a description of a part.
|
|
*
|
|
* b bytes are "swapped" (not in PDP-11 order)
|
|
* w words are swapped
|
|
* p struct is "packed" (no padding for alignment)
|
|
* <cnt> count of times to repeat following type
|
|
* <type> one of the following:
|
|
* c char
|
|
* s short
|
|
* l long
|
|
*
|
|
* Example: given the struct
|
|
*
|
|
* struct
|
|
* {
|
|
* short x;
|
|
* short y;
|
|
* char z[16];
|
|
* long w;
|
|
* };
|
|
*
|
|
* and assuming it is to be written so as to use VAX byte- and
|
|
* word-ordering, its dope vector would be:
|
|
*
|
|
* "wss16cl"
|
|
*/
|
|
|
|
int swrite(cp,dopevec,count,file)
|
|
char *cp; /* Pointer to struct array */
|
|
char *dopevec; /* Dope vector for struct */
|
|
int count; /* Number of structs in array */
|
|
FILE *file; /* File to write to */
|
|
{
|
|
int pack; /* Packed flag */
|
|
int rpt; /* Repeat count */
|
|
REGISTER int cc = 0; /* Count of characters written */
|
|
REGISTER char *dv; /* Dope vector less flags */
|
|
short *sp; /* Pointer to short */
|
|
long *lp; /* Pointer to long */
|
|
|
|
bswap = wswap = pack = 0; /* Initialize flags */
|
|
while(*dopevec != '\0') /* Loop to set flags */
|
|
{
|
|
if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */
|
|
else if(*dopevec == 'p') pack = 1;
|
|
/* Check for packed flag */
|
|
else if(*dopevec == 'w') wswap = 1;
|
|
/* Check for word-swapped flag */
|
|
else break;
|
|
++dopevec;
|
|
}
|
|
while(count-- > 0) /* Main loop */
|
|
{
|
|
dv = dopevec; /* Initialize */
|
|
for(;;) /* Loop to write struct */
|
|
{
|
|
if(*dv >= '0' && *dv <= '9')
|
|
{ /* If there is a repeat count */
|
|
rpt = 0; /* Initialize */
|
|
do /* Loop to get repeat count */
|
|
{
|
|
rpt = rpt*10 + *dv++ - '0';
|
|
/* Take digit */
|
|
}
|
|
while(*dv >= '0' && *dv <= '9');
|
|
/* Loop until non-digit found */
|
|
}
|
|
else rpt = 1; /* Else repeat count defaults to one */
|
|
if(*dv == '\0') break; /* break if end of dope vector */
|
|
switch(*dv++) /* Switch on type character */
|
|
{
|
|
case 'c': /* Character */
|
|
#if FALSE AND OEXE
|
|
CheckSum(rpt, cp);
|
|
#endif
|
|
if(fwrite(cp,sizeof(char),rpt,file) != rpt) return(cc);
|
|
/* Write the characters */
|
|
cp += rpt; /* Increment pointer */
|
|
cc += rpt; /* Increment count of bytes written */
|
|
break;
|
|
|
|
case 's': /* Short */
|
|
if(!pack && (cc & 1)) /* If not packed and misaligned */
|
|
{
|
|
cput(*cp++,file); /* Write padding byte */
|
|
++cc; /* Increment byte count */
|
|
}
|
|
sp = (short *) cp; /* Initialize pointer */
|
|
while(rpt-- > 0) /* Loop to write shorts */
|
|
{
|
|
(*fpstab[bswap])(*sp++,file);
|
|
/* Write the short */
|
|
if(feof(file) || ferror(file)) return(cc);
|
|
/* Check for errors */
|
|
cc += sizeof(short);
|
|
/* Increment byte count */
|
|
}
|
|
cp = (char *) sp; /* Update pointer */
|
|
break;
|
|
|
|
case 'l': /* Long */
|
|
if(!pack && (cc & 3)) /* If not packed and misaligned */
|
|
{
|
|
while(cc & 3) /* While not aligned */
|
|
{
|
|
cput(*cp++,file);
|
|
/* Write padding byte */
|
|
++cc; /* Increment byte count */
|
|
}
|
|
}
|
|
lp = (long *) cp; /* Initialize pointer */
|
|
while(rpt-- > 0) /* Loop to write longs */
|
|
{
|
|
(*fpltab[wswap])(*lp++,file);
|
|
/* Write the long */
|
|
if(feof(file) || ferror(file)) return(cc);
|
|
/* Check for errors */
|
|
cc += sizeof(long);
|
|
/* Increment byte count */
|
|
}
|
|
cp = (char *) lp; /* Update pointer */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(cc); /* Return count of bytes written */
|
|
}
|
|
|
|
/*
|
|
* int sread(cp,dopevec,count,file)
|
|
* char *cp;
|
|
* char *dopevec;
|
|
* int count;
|
|
* FILE *file;
|
|
*
|
|
* Returns number of bytes read.
|
|
*
|
|
* Dopevec is a character string whose format is described
|
|
* with swrite() above.
|
|
*/
|
|
int sread(cp,dopevec,count,file)
|
|
char *cp; /* Pointer to struct array */
|
|
char *dopevec; /* Dope vector for struct */
|
|
int count; /* Number of structs in array */
|
|
FILE *file; /* File to read from */
|
|
{
|
|
int pack; /* Packed flag */
|
|
int rpt; /* Repeat count */
|
|
REGISTER int cc = 0; /* Count of characters written */
|
|
REGISTER char *dv; /* Dope vector less flags */
|
|
short *sp; /* Pointer to short */
|
|
long *lp; /* Pointer to long */
|
|
|
|
bswap = wswap = pack = 0; /* Initialize flags */
|
|
while(*dopevec != '\0') /* Loop to set flags */
|
|
{
|
|
if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */
|
|
else if(*dopevec == 'p') pack = 1;
|
|
/* Check for packed flag */
|
|
else if(*dopevec == 'w') wswap = 1;
|
|
/* Check for word-swapped flag */
|
|
else break;
|
|
++dopevec;
|
|
}
|
|
while(count-- > 0) /* Main loop */
|
|
{
|
|
dv = dopevec; /* Initialize */
|
|
for(;;) /* Loop to write struct */
|
|
{
|
|
if(*dv >= '0' && *dv <= '9')
|
|
{ /* If there is a repeat count */
|
|
rpt = 0; /* Initialize */
|
|
do /* Loop to get repeat count */
|
|
{
|
|
rpt = rpt*10 + *dv++ - '0';
|
|
/* Take digit */
|
|
}
|
|
while(*dv >= '0' && *dv <= '9');
|
|
/* Loop until non-digit found */
|
|
}
|
|
else rpt = 1; /* Else repeat count defaults to one */
|
|
if(*dv == '\0') break; /* break if end of dope vector */
|
|
switch(*dv++) /* Switch on type character */
|
|
{
|
|
case 'c': /* Character */
|
|
if(fread(cp,sizeof(char),rpt,file) != rpt) return(cc);
|
|
/* Read the characters */
|
|
cp += rpt; /* Increment pointer */
|
|
cc += rpt; /* Increment count of bytes written */
|
|
break;
|
|
|
|
case 's': /* Short */
|
|
if(!pack && (cc & 1)) /* If not packed and misaligned */
|
|
{
|
|
*cp ++ = cget(file);
|
|
/* Read padding byte */
|
|
++cc; /* Increment byte count */
|
|
}
|
|
sp = (short *) cp; /* Initialize pointer */
|
|
while(rpt-- > 0) /* Loop to read shorts */
|
|
{
|
|
*sp++ = (*fgstab[bswap])(file);
|
|
/* Read the short */
|
|
if(feof(file) || ferror(file)) return(cc);
|
|
/* Check for errors */
|
|
cc += sizeof(short);
|
|
/* Increment byte count */
|
|
}
|
|
cp = (char *) sp; /* Update pointer */
|
|
break;
|
|
|
|
case 'l': /* Long */
|
|
if(!pack && (cc & 3)) /* If not packed and misaligned */
|
|
{
|
|
while(cc & 3) /* While not aligned */
|
|
{
|
|
*cp++ = cget(file);
|
|
/* Read padding byte */
|
|
++cc; /* Increment byte count */
|
|
}
|
|
}
|
|
lp = (long *) cp; /* Initialize pointer */
|
|
while(rpt-- > 0) /* Loop to read longs */
|
|
{
|
|
*lp++ = (*fgltab[wswap])(file);
|
|
/* Read the long */
|
|
if(feof(file) || ferror(file)) return(cc);
|
|
/* Check for errors */
|
|
cc += sizeof(long);
|
|
/* Increment byte count */
|
|
}
|
|
cp = (char *) lp; /* Update pointer */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(cc); /* Return count of bytes written */
|
|
}
|
|
#endif
|
|
|
|
#define CB_POOL 4096
|
|
|
|
typedef struct _POOLBLK
|
|
{
|
|
struct _POOLBLK * pblkNext; // next pool in list
|
|
int cb; // number of bytes in this pool (free+alloc)
|
|
char rgb[1]; // data for this pool (variable sized)
|
|
} POOLBLK;
|
|
|
|
typedef struct _POOL
|
|
{
|
|
struct _POOLBLK * pblkHead; // start of poolblk list
|
|
struct _POOLBLK * pblkCur; // current poolblk we are searching
|
|
int cb; // # bytes free in current pool
|
|
char * pch; // pointer to free data in current pool
|
|
} POOL;
|
|
|
|
void *
|
|
PInit()
|
|
{
|
|
POOL *ppool;
|
|
|
|
// create new pool, set size and allocate CB_POOL bytes
|
|
|
|
ppool = (POOL *)GetMem(sizeof(POOL));
|
|
ppool->pblkHead = (POOLBLK *)GetMem(sizeof(POOLBLK) + CB_POOL-1);
|
|
ppool->pblkHead->cb = CB_POOL;
|
|
ppool->pblkHead->pblkNext = NULL;
|
|
ppool->cb = CB_POOL;
|
|
ppool->pch = &ppool->pblkHead->rgb[0];
|
|
ppool->pblkCur = ppool->pblkHead;
|
|
|
|
return (void *)ppool;
|
|
}
|
|
|
|
void *
|
|
PAlloc(void *pp, int cb)
|
|
{
|
|
POOL *ppool = (POOL *)pp;
|
|
void *pchRet;
|
|
POOLBLK *pblkCur, *pblkNext;
|
|
|
|
// if the allocation doesn't fit in the current block
|
|
|
|
if (cb > ppool->cb)
|
|
{
|
|
pblkCur = ppool->pblkCur;
|
|
pblkNext = pblkCur->pblkNext;
|
|
|
|
// then check the next block
|
|
|
|
if (pblkNext && pblkNext->cb >= cb)
|
|
{
|
|
// set the master info to reflect the next page...
|
|
|
|
ppool->pblkCur = pblkNext;
|
|
ppool->cb = pblkNext->cb;
|
|
ppool->pch = &pblkNext->rgb[0];
|
|
memset(ppool->pch, 0, ppool->cb);
|
|
}
|
|
else
|
|
{
|
|
POOLBLK *pblkNew; // new pool
|
|
|
|
// allocate new memory -- at least enough for this allocation
|
|
pblkNew = (POOLBLK *)GetMem(sizeof(POOLBLK)+cb+CB_POOL-1);
|
|
pblkNew->cb = CB_POOL + cb;
|
|
|
|
// link the current page to the new page
|
|
|
|
pblkNew->pblkNext = pblkNext;
|
|
pblkCur->pblkNext = pblkNew;
|
|
|
|
// set the master info to reflect the new page...
|
|
|
|
ppool->pblkCur = pblkNew;
|
|
ppool->cb = CB_POOL + cb;
|
|
ppool->pch = &pblkNew->rgb[0];
|
|
}
|
|
|
|
}
|
|
|
|
pchRet = (void *)ppool->pch;
|
|
ppool->pch += cb;
|
|
ppool->cb -= cb;
|
|
return pchRet;
|
|
}
|
|
|
|
void
|
|
PFree(void *pp)
|
|
{
|
|
POOL *ppool = (POOL *)pp;
|
|
POOLBLK *pblk = ppool->pblkHead;
|
|
POOLBLK *pblkNext;
|
|
|
|
while (pblk)
|
|
{
|
|
pblkNext = pblk->pblkNext;
|
|
FFREE(pblk);
|
|
pblk = pblkNext;
|
|
}
|
|
|
|
FFREE(ppool);
|
|
}
|
|
|
|
void
|
|
PReinit(void *pp)
|
|
{
|
|
POOL *ppool = (POOL *)pp;
|
|
|
|
ppool->pblkCur = ppool->pblkHead;
|
|
ppool->cb = ppool->pblkHead->cb;
|
|
ppool->pch = &ppool->pblkHead->rgb[0];
|
|
|
|
memset(ppool->pch, 0, ppool->cb);
|
|
}
|
|
|
|
#if RGMI_IN_PLACE
|
|
|
|
/****************************************************************
|
|
* *
|
|
* PchSegAddress: *
|
|
* *
|
|
* compute the address that will hold this data so we can read *
|
|
* it in place... we make sure that we can read in place at *
|
|
* and give errors as in MoveToVm if we cannot *
|
|
* *
|
|
* Input: cb Count of bytes to be moved. *
|
|
* seg Logical segment to which data belongs. *
|
|
* ra Offset at which data belongs. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
BYTE FAR * PchSegAddress(WORD cb, SEGTYPE seg, RATYPE ra)
|
|
{
|
|
long cbtot; /* Count of bytes total */
|
|
long cbSeg; /* Segment size */
|
|
WORD fError;
|
|
BYTE FAR *pMemImage;
|
|
CVINFO FAR *pCVInfo;
|
|
SATYPE sa;
|
|
|
|
cbtot = (long) cb + ra;
|
|
|
|
if (fDebSeg)
|
|
{
|
|
pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo;
|
|
if (pCVInfo)
|
|
{
|
|
if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal))
|
|
{
|
|
cbSeg = pCVInfo->cv_cbTyp;
|
|
pMemImage = pCVInfo->cv_typ;
|
|
|
|
if (!pMemImage)
|
|
pCVInfo->cv_typ = pMemImage = GetMem(cbSeg);
|
|
}
|
|
else
|
|
{
|
|
cbSeg = pCVInfo->cv_cbSym;
|
|
pMemImage = pCVInfo->cv_sym;
|
|
|
|
if (!pMemImage)
|
|
pCVInfo->cv_sym = pMemImage = GetMem(cbSeg);
|
|
}
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = cbtot > cbSeg;
|
|
}
|
|
else
|
|
{
|
|
OutError(ER_badcvseg);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fNewExe)
|
|
{
|
|
cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx;
|
|
sa = mpsegsa[seg];
|
|
if (mpsaMem[sa] == NULL)
|
|
mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]);
|
|
pMemImage = mpsaMem[sa];
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg;
|
|
}
|
|
else
|
|
{
|
|
cbSeg = mpsegcb[seg] + mpsegraFirst[seg];
|
|
if (mpsegMem[seg] == NULL)
|
|
mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg);
|
|
pMemImage = mpsegMem[seg];
|
|
|
|
// Check against segment bounds
|
|
|
|
fError = cbtot > cbSeg;
|
|
}
|
|
}
|
|
|
|
if (fError)
|
|
{
|
|
if (!fDebSeg)
|
|
OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch));
|
|
else
|
|
OutError(ER_badcvseg);
|
|
}
|
|
|
|
return (pMemImage + ra);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if USE_REAL
|
|
|
|
// Indicates if you are running under TNT.
|
|
// If it returns FALSE today, you are running on NT.
|
|
|
|
int IsDosxnt ( ) {
|
|
|
|
#if defined( _WIN32 )
|
|
return FALSE;
|
|
#else
|
|
HINSTANCE hLib = GetModuleHandle("kernel32.dll");
|
|
if ( hLib != 0 && (GetProcAddress(hLib, "IsTNT") != 0)) {
|
|
return(TRUE);
|
|
}
|
|
else {
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
// Are we running on Win31 or greater.
|
|
// Note that we know if we are running under Windows we are running in enhanced mode.
|
|
|
|
int IsWin31() {
|
|
|
|
#if defined( _WIN32 )
|
|
return FALSE;
|
|
#else
|
|
__asm {
|
|
mov ax,1600h ; Is Win31 or greater running
|
|
int 2fh
|
|
cmp al,03h ; Is major version number 3.0
|
|
jb NotWin31 ; Major version less than 3.0
|
|
ja ItIsWin31
|
|
cmp ah,0ah ; Is minor version atleast .10
|
|
jb NotWin31 ; Must be Win3.0
|
|
}
|
|
ItIsWin31:
|
|
return (TRUE);
|
|
NotWin31:
|
|
return (FALSE);
|
|
#endif // NOT _WIN32
|
|
}
|
|
|
|
int MakeConvMemPageable ( )
|
|
{
|
|
#if defined( _WIN32 )
|
|
return TRUE;
|
|
#else
|
|
if ( realModeMemPageable ) {
|
|
return ( TRUE ); // Somebody already freed the real mode mem.
|
|
}
|
|
__asm {
|
|
mov ax,0100h ; function to get DOS memory.
|
|
mov bx,TOTAL_CONV_MEM ; Ask for 1 M to get max memory count
|
|
int 31h
|
|
|
|
jnc errOut ; allocated 1 M - something must be wrong.
|
|
|
|
cmp ax,08h ; Did we fail because of not enough memory
|
|
jne errOut ; No we failed because of some other reason.
|
|
cmp bx,MIN_CONV_MEM ; See if we can allocate atleast the min
|
|
|
|
// We could fail for two reasons here .
|
|
// 1) we really didn't have sufficient memory.
|
|
// 2) Some TNT app that spawned us already unlocked this memory. For ex:
|
|
// cl might have already freed up the memory when it calls link.exe.
|
|
|
|
jb errOut ; Too little mem available don't bother.
|
|
|
|
sub bx,CONV_MEM_FOR_TNT ; Leave real mode mem for TNT.
|
|
mov ax,0100h ; Try again with new amount of memory
|
|
int 31h ; Ask for the real mode memory from DPMI.
|
|
jc errOut ; didn't succeed again, give up.
|
|
|
|
mov convMemSelector,dx ; Save the value of the selector for allocated block
|
|
mov noOfParagraphs,bx ; amount of memory we were able to allocate.
|
|
|
|
mov ax,0006h ; function to get base addr of a selector
|
|
mov bx,dx ; move the selector to bx
|
|
int 31h ; Get Segment Base Address
|
|
jc errOut ;
|
|
|
|
mov bx,cx ; mov lin addr from cx:dx to bx:cx
|
|
mov cx,dx ;
|
|
|
|
movzx eax,noOfParagraphs
|
|
shl eax,4 ; Multiply by 16 to get count in bytes.
|
|
|
|
mov di,ax ; transfer size to si:di from eax
|
|
shr eax,16 ;
|
|
mov si,ax ;
|
|
|
|
mov ax,602h ; Make real mode memory pageable
|
|
int 31h
|
|
|
|
jc errOut ; Didn't work.
|
|
|
|
mov ax,703h ; Indicate data in these pages is discardable.
|
|
int 31h
|
|
// Even if we fail this call, we will still assume we are succesful,
|
|
// because it is just a performance enhancement
|
|
// Also for correctness we should relock the memory once it is free.
|
|
}
|
|
realModeMemPageable = TRUE ;
|
|
errOut:
|
|
return(realModeMemPageable);
|
|
#endif // NOT _WIN32
|
|
}
|
|
|
|
/* Relock the real mode memory now */
|
|
|
|
int RelockConvMem ( void )
|
|
{
|
|
#if defined( _WIN32 )
|
|
return TRUE;
|
|
#else
|
|
if ( !realModeMemPageable ) {
|
|
return ( TRUE ); // We were never able to free the mem anyway.
|
|
}
|
|
__asm {
|
|
mov bx, convMemSelector
|
|
mov ax, 0006h
|
|
int 31h ; Get Segment Base Address.
|
|
jc errOut ;
|
|
|
|
mov bx,cx ; Mov lin addr from cx:dx to bx:cx
|
|
mov cx,dx
|
|
|
|
movzx eax,noOfParagraphs
|
|
shl eax,4 ; Mul paragraphs by 16 to get count in bytes.
|
|
|
|
mov di,ax ;Transfer size to si:di from eax.
|
|
shr eax,16
|
|
mov si,ax
|
|
|
|
mov ax,603h ; Relock real mode region
|
|
int 31h
|
|
jc errOut
|
|
|
|
mov dx,convMemSelector
|
|
mov ax,101h ; Free the real mode memory
|
|
int 31h
|
|
jc errOut
|
|
}
|
|
realModeMemPageable = FALSE ;
|
|
return ( TRUE );
|
|
errOut:
|
|
return ( FALSE );
|
|
#endif // NOT _WIN32
|
|
}
|
|
|
|
void RealMemExit(void)
|
|
{
|
|
if(fUseReal)
|
|
{
|
|
if(!RelockConvMem())
|
|
OutError(ER_membad);
|
|
fUseReal = FALSE;
|
|
}
|
|
}
|
|
#endif
|