16-bit DOS .EXE files
All multi-byte values are LITTLE ENDIAN

================================================================
File header
================================================================
file
offset
     -----------------------------------------------
 0  |     EXE file magic    | modulus of file size  |
     -----------------------------------------------
 4  | file size, in sectors |   num. relocations    |
     -----------------------------------------------
 8  |      header size      |     minimum heap      |
     -----------------------------------------------
0Ch |      maximum heap     |      initial SS       |
     -----------------------------------------------
10h |      initial SP       |       checksum        |
     -----------------------------------------------
14h |      initial CS       |      initial IP       |
     -----------------------------------------------
18h |  reloc. table offset  |    overlay number     |
     -----------------------------------------------
1Ch |       0001h           |
     -----------------------

bytes at offsets 1Eh ... 21h are used by debuggers, etc.
bytes at offsets 22h ... 3Bh are reserved, and should =0

     -----------------------------------------------
3Ch |       offset of new executable header         |
     -----------------------------------------------
40h

EXE file magic
        =5A4Dh ("MZ")
modulus of file size
	=file size % 512 (remainder after dividing by 512)
	A value of zero means the entire 512 bytes are used
	(note that the next field is rounded up).
file size, in sectors
	=file size / 512, rounded up

	EXE size is computed from these two values like this:

	exe_size = file_size * 512uL;
	if(mod_file_size != 0)
		exe_size = exe_size - 512 + mod_file_size;

	This value need not match the actual file size returned
	by the operating system. Difference in sizes could be
	due to:
	- debug information appended to the end of the file
	- 32-bit code appended to the EXE file, which probably
	  contains a DOS extender 'stub'
header size
        =size of .EXE file header and relocations, in 16-byte
        paragraphs. This value is the file offset of the
	"load module" (combined code and data segments)
	Some OSes and/or programs may fail if the header is
	not a multiple of 512 bytes.
minimum heap
        =minimum number of paragraphs of memory to be
         allocated after code and data, for BSS/heap/stack

         Note: if you compile -or- link a Turbo C program
         with debug info, the BSS will be moved into the
         initialized data segment, and this value may = 0
maximum heap
        =maximum number of paragraphs of memory to be
         allocated after code and data, usu. = 65535
initial SS
        relative to start of combined code/data segment. "Offset
        in load module of stack segment (in paragraphs)."
checksum
        =0 if none. Otherwise, this is the negative of the sum
         of all the 16-bit words in the file, ignoring overflow
initial CS
        relative to start of combined code/data segment. "Offset
        in load module of the code segment (in paragraphs)."

        Because C-language startup code is usually placed
        at the beginning of the file, initial CS and IP
        usually =0
reloc. table offset
        =40h for new executable (NE,PE,LE,LX,W3 etc.)
        otherwise this is the offset (in BYTES) of the
        relocation table
overlay number
        =0 for root program
offset of new executable header
        =0 for normal DOS .EXE
        This field is not valid unless header size >= 4

================================================================
Relocations
================================================================
Relocation entries are 16:16 far addresses, each one relative to
the start of the file (xxx - or start of "load module"?)

Once the relocatable item is found, the CS register is added to
the value found at the calculated offset.

xxx - code

struct EXE {
  unsigned short signature; /* == 0x5a4D */
  unsigned short bytes_in_last_block;
  unsigned short blocks_in_file;
  unsigned short num_relocs;
  unsigned short header_paragraphs;
  unsigned short min_extra_paragraphs;
  unsigned short max_extra_paragraphs;
  unsigned short ss;
  unsigned short sp;
  unsigned short checksum;
  unsigned short ip;
  unsigned short cs;
  unsigned short reloc_table_offset;
  unsigned short overlay_number;
};

struct EXE_RELOC {
  unsigned short offset;
  unsigned short segment;
};
