Category : C Source Code
Archive   : EXECOM33.ZIP
Filename : EXE2COM.C86

Output of file : EXE2COM.C86 contained in archive : EXECOM33.ZIP
exe2com - exe2bin replacement by Chris Dunford/Cove Software

usage: exe2com file [file]
usage is the same as exe2bin except:
1. Output defaults to COM rather than BIN
2. Binary fixup option not supported
3. Checksum not verified
4. Provides more useful error messages, and a warning if a
COM file is being created with initial IP != 0x100.

Compiler notes:
The original executable of this file was produced with the
Computer Innovations C86 compiler (v2.2), but it should work
with just about any C compiler, with the possible exception
of the lower() function. This function is part of the C86
library; it simply lowercases a string without moving it and
returns the address of the string. If your compiler doesn't
have lower() or an equivalent, add this function:

char *lower (cp)
char *cp;
char *cp0;

for (cp0=cp; *cp=tolower (*cp); ++cp)
return cp0;

This program was knocked together in about an hour in response to
the removal of EXE2BIN from the standard DOS distribution disks.
Improvements/corrections are encouraged.

Program donated to the public domain by the author.

cjd 4/17/87

Fix for even 512-byte boundary file losing last 512 bytes.
Also corrected signon per request of Chris Dunford ( his name
was lost in the translation to Turbo C ).
Version updated to 1.02 to indicate change.

Chris Blum CompuServe 76625,1041

CJB 11/22/87



#define void int

/* Conversion error codes */
#define BADREAD 1
#define BADWRITE 2
#define BADSIG 3
#define HASRELO 4
#define HAS_SS 5
#define HAS_CS 6
#define BAD_IP 7
#define TOO_BIG 8

** Define structure of fixed-format part of EXE file header
struct exe_header {
char exe_sig[2]; /* EXE file signature: "MZ" */
unsigned excess, /* Image size mod 512 (valid bytes in last page) */
pages, /* # 512-byte pages in image */
relo_ct, /* Count of relocation table entries */
hdr_size, /* Size of header, in paragraphs */
min_mem, /* Min required memory */
max_mem, /* Max required memory */
ss, /* Stack seg offset in load module */
sp, /* Initial value of SP */
cksum, /* File checksum */
ip, /* Initial value of IP */
cs, /* CS offset in load module */
relo_start, /* Offset of first relo item */
ovl_num; /* Overlay number */
} xh;

FILE *fi, /* Input file stream */
*fo, /* Output file stream */

char fin[129], /* Input file name */
fon[129]; /* Output file name */

unsigned long code_start, /* Offset of program image in EXE file */
code_size; /* Size of program image, in bytes */

extern char *strcpy(), *strcat(), *index(), *lower();

main(argc, argv)
unsigned argc;
char *argv[];
init (argc, argv);
read_hdr ();
convert ();

** Initialize - get filenames and open/create files
void init (argc, argv)
unsigned argc;
char *argv[];
char *cp;

printf ("exe2com 1.02 by Chris Dunford/The Cove Software Group\n");

/* Check arg count */
if (argc < 2 || argc > 3) {
fprintf (stderr, "usage: exe2com file [file]\n");
exit (1);

/* If argv[1] (the input file) has no extension, add .EXE */
strcpy (fin, lower (argv[1]));
if (!index (fin, '.'))
strcat (fin, ".exe");

/* Get or construct output file name */
if (argc == 3)
strcpy (fon, lower (argv[2]));
strcpy (fon, fin);

/* Check output extension--change EXE to COM, or add COM */
if (!(cp = index (fon, '.')))
strcat (fon, ".com");
else if (strcmp (cp, ".exe") == 0)
strcpy (cp, ".com");

#ifdef DEBUG
printf ("input=%s, output=%s\n", fin, fon);

/* Try to open input file */
if (!(fi = fopen (fin, "rb"))) {
fprintf (stderr, "exe2com: can't find input file %s\n", fin);
exit (1);

/* Try to create output file */
if (!(fo = fopen (fon, "wb"))) {
fprintf (stderr, "exe2com: can't open output file %s\n", fin);
exit (1);

** Read and check the EXE file header
void read_hdr()

/* Read the formatted portion of the header */
if (!fread (&xh, sizeof (struct exe_header), 1, fi))
err_xit (BADREAD);

#ifdef DEBUG
printf ("EXE file header:\n");
printf (" signature %c%c\n", xh.exe_sig[0], xh.exe_sig[1]);
printf (" bytes last pg %04x\n", xh.excess);
printf (" pages %04x\n", xh.pages);
printf (" relo count %04x\n", xh.relo_ct);
printf (" header size %04x\n", xh.hdr_size);
printf (" min mem %04x\n", xh.min_mem);
printf (" max mem %04x\n", xh.max_mem);
printf (" ss %04x\n",;
printf (" sp %04x\n", xh.sp);
printf (" checksum %04x\n", xh.cksum);
printf (" ip %04x\n", xh.ip);
printf (" cs %04x\n", xh.cs);
printf (" relo tbl start %04x\n", xh.relo_start);
printf (" overlay nbr %04x\n", xh.ovl_num);

/* Check header; to be convertible, must have:
** -- first two bytes == "MZ"
** -- no relocatable items
** -- no stack segment
** -- no code segment
** -- IP == 0 or 100
if (strncmp (xh.exe_sig, "MZ", 2))
err_xit (BADSIG);
if (xh.relo_ct)
err_xit (HASRELO);
if ( || xh.sp)
err_xit (HAS_SS);
if (xh.ip != 0 && xh.ip != 0x100)
err_xit (BAD_IP);

/* Compute offset of program image in module, and program size.
** The program size is computed as follows; it cannot exceed 64K bytes:
** 512 * (# EXE pages - 1)
** + valid bytes in last EXE page
** - offset of program image in EXE file
** Note that if the IP is nonzero, we will skip the first
** IP bytes of the program image, and copy IP bytes fewer
** than the actual size.
code_start = ((unsigned long) xh.hdr_size) << 4;
code_size = (unsigned long) (xh.pages-1) * 512
+ (xh.excess ? xh.excess : 512) /* fixed 11/19/87 - CJB */
- code_start;
if (code_size > 65536L)
err_xit (TOO_BIG);

/* Issue a warning if COM file and IP != 0x100 */
if (!strcmp (index (fon, '.'), ".com") && xh.ip != 0x100)
fprintf (stderr, "exe2com warning: COM file, initial IP not 100H\n");


** Convert the file. Nothing to do, really, other than
** reading the image (which follows the header), and
** dumping it back out to disk.
void convert ()
char buffer[512];
unsigned wsize;
extern unsigned long fseek();

/* Seek to start of program image, skipping IP bytes */
if (fseek (fi, code_start+xh.ip, 0) != code_start+xh.ip)
err_xit (BADREAD);

/* Reduce the "remaining" byte count by IP bytes */
code_size -= xh.ip;

/* Read blocks and copy to output */
while (code_size) {

/* Read block */
if (!fread (buffer, 512, 1, fi))
err_xit (BADREAD);

/* Set count of bytes to write, write block */
wsize = code_size > 512 ? 512 : code_size;
if (!fwrite (buffer, wsize, 1, fo))
err_xit (BADWRITE);

/* Subtract bytes written from remaining byte count */
code_size -= wsize;

/* All done, close the two files */
fclose (fi);
fclose (fo);

** Display an error message, delete output file, exit.

void err_xit (code)
unsigned code;
char msg[64];

switch (code) {
case BADREAD: strcpy (msg, "error reading EXE header");
case BADWRITE: strcpy (msg, "error writing output file");
case BADSIG: strcpy (msg, "invalid EXE file signature");
case HASRELO: strcpy (msg, "EXE has relocatable items");
case HAS_SS: strcpy (msg, "EXE has stack segment");
case HAS_CS: strcpy (msg, "EXE has nonzero CS");
case BAD_IP: strcpy (msg, "IP not 0 or 100H");
case TOO_BIG: strcpy (msg, "program exceeds 64K");
default: strcpy (msg, "unknown error");

fprintf (stderr, "exe2com: %s, can't convert\n", msg);

/* Close two files and delete partial output */
fclose (fi);
fclose (fo);
unlink (fon);

/* Exit with errorlevel 1 */
exit (1);

  3 Responses to “Category : C Source Code
Archive   : EXECOM33.ZIP
Filename : EXE2COM.C86

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: