Title  EXECOM: convert an .EXE file to a .COM file

comment !
   EXECOM 1.1  Copyright (c) 1988  Thomas G. Hanlin III

         1712 Maple Hill Pl.
         Alexandria VA 22152

   Note: This was assembled with the excellent OPTASM assembler.  Slight
   modifications may be needed in the way of JMP pointers, MOV size
   pointers, and so forth if you're using MASM (yuck).
!


Sseg           segment byte stack 'prog'    ; dummy stack segment
Sseg           ends


Cseg           segment byte public 'prog'
               assume         cs:Cseg, ds:Cseg, ss:Sseg

               org            100h

MAIN           proc           far
               xor            dx,dx              ; number of parms
               mov            cx,dx
               mov            EXITCODE,cl        ; assume no errors
               mov            cl,ds:[0080h]      ; get parm length
               jcxz           Inform             ;   inform 'em if no parms
               mov            si,0081h
               cld

ScanParms:     lodsb                             ; get a command-line char
               cmp            al," "             ; is it a space?
               jbe            ScanLoop2          ;   yes, ignore it
               mov            di,offset OUTFILE  ; assume output file name
               inc            dx                 ; increment parm number
               cmp            dx,2               ; check it
               ja             Inform             ;   too large, inform 'em
               je             SaveFileName       ;   default OUTFILE is ok
               mov            di,offset INFILE   ; input file name (string)
SaveFileName:  stosb                             ; store a char of the filename
               dec            cx                 ; are we done yet?
               jz             ScanDone           ;   yes, exit
               lodsb                             ; get another char
               cmp            al," "             ; is it blank?
               ja             SaveFileName       ;   no, must be a filename chr
ScanLoop2:     loop           ScanParms          ;   keep looking for parms

ScanDone:      or             dx,dx              ; any parms?
               jz             Inform             ;   no, inform 'em
               dec            dx                 ; only one parm?
               jnz            ChangeExts         ;   no, go change extensions
               mov            si,offset INFILE   ; input file name
               mov            di,offset OUTFILE  ; output file name
DupeFile:      lodsb                             ; get an input char
               or             al,al              ; is it null?
               jz             ChangeExts         ;   yes, done- change exts
               stosb                             ; store char in output name
               jmp            DupeFile           ;   keep on truckin'

Inform:        mov            dx,offset INFORMATION
               mov            ah,9
               int            21h
               mov            EXITCODE,255
               jmp            Abort

ChangeExts:    mov            si,offset INFILE   ; input file name
Change1:       lodsb                             ; get a char
               or             al,al              ; are we done?
               jz             AddExtIn1          ;   yes, needs extension
               cmp            al,"."             ; is it a period?
               jne            Change1            ;   no, keep looking

ChangeExt2:    mov            si,offset OUTFILE  ; output file name
Change2:       lodsb                             ; get a char
               or             al,al              ; are we done?
               jz             AddExtIn2          ;   yes, needs extension
               cmp            al,"."             ; is it a period?
               jne            Change2            ;   no, keep looking
               lodsw                             ; is the extension ".EXE"?
               cmp            ax,"XE"            ;
               jne            OpenFiles          ;   no, go open the files
               lodsb                             ;
               cmp            al,"E"             ;
               jne            OpenFiles          ;   no, go open the files
               sub            si,3               ; pretend there's no ext

AddExtIn2:     dec            si                 ; move back to the first null
               mov            di,si
               mov            ax,"C."            ; install a ".COM"...
               stosw                             ; ...file extension
               mov            ax,"MO"            ;
               stosw                             ;
               jmp            OpenFiles

AddExtIn1:     dec            si                 ; move back to the first null
               mov            di,si
               mov            ax,"E."            ; install an ".EXE"...
               stosw                             ; ...file extension
               mov            ax,"EX"            ;
               stosw                             ;
               jmp            ChangeExt2


;------------------- Everything's set up, let's open the files ----------------


OpenFiles:     mov            ax,3D00h           ; open file for read
               mov            dx,offset INFILE   ; input file name
               int            21h
               jnc            InputOpenOk
               mov            bl,1
               jmp            ErrVector

InputOpenOk:   mov            INHANDLE,ax        ; save input file handle
               mov            bx,ax
               mov            ah,3Fh             ; read from file
               mov            cx,24              ; 24 bytes
               mov            dx,offset LASTBYTE ; data buffer
               int            21h
               jnc            ReadOk
               mov            bl,9
               jmp            ErrVector

ReadOk:        mov            si,dx
               mov            di,offset EXE_SIG
               movsw                             ; set EXE_SIG
               movsw                             ; set EXE_EXCESS
               movsw                             ; set EXE_PAGES
               movsw                             ; set EXE_RELOCCOUNT
               movsw                             ; set EXE_HDRSIZE
               add            si,4
               movsw                             ; set EXE_SS
               movsw                             ; set EXE_SP
               add            si,2
               movsw                             ; set EXE_IP
               movsw                             ; set EXE_CS


               mov            bl,3               ; default error code
               cmp            EXE_SIG,"ZM"       ; is the signature ok?
               jne            ErrVector          ;   nope, error
               inc            bl                 ; inc error code
               cmp            EXE_RELOCCOUNT,0   ; any relocations needed?
               jne            ErrVector          ;   yes, error
               inc            bl                 ; inc error code
               cmp            EXE_SS,0           ; is there a defined SS?
               jne            ErrVector          ;   yes, error
               cmp            EXE_SP,0           ; is there a defined SP?
               jne            ErrVector          ;   yes, error
               inc            bl                 ; inc error code
               cmp            EXE_CS,0           ; is there a defined CS?
               jne            ErrVector          ;   yes, error
               inc            bl                 ; inc error code
               cmp            EXE_IP,0           ; is the IP = 0?
               je             ChecksOk           ;   yes, it's ok
               cmp            EXE_IP,0100h       ; is the IP = 0100h?
               jne            ErrVector          ;   no, error

ChecksOk:      mov            bx,EXE_HDRSIZE
               mov            cl,4
               shl            bx,cl              ; CodeStart in BX ****
               mov            ax,EXE_PAGES
               dec            ax
               mov            cx,512
               mul            cx
               sub            ax,bx
               sbb            dx,0               ; CodeSize in DX:AX ***
               mov            cx,EXE_EXCESS
               jcxz           NoDribble
               add            ax,cx
               jmp            GotCodeSize
NoDribble:     add            ax,512
GotCodeSize:   adc            dx,0
               or             dx,dx              ; is it too large?
               jz             CheckIP            ;   no, keep on truckin'
               mov            bl,8
ErrVector:     jmp            ErrorExit

CheckIP:       push           ax
               cmp            EXE_IP,0100h       ; is IP right for .COM files?
               je             IPok               ;   yes...
               mov            dx,offset WARNING
               mov            ah,9               ; display warning message
               int            21h
IPok:          mov            dx,bx              ; CodeStart
               xor            cx,cx
               add            dx,EXE_IP
               adc            cx,cx
               mov            bx,INHANDLE
               mov            ax,4200h           ; move file ptr from start
               int            21h
               mov            ah,3Ch             ; create file for write
               mov            dx,offset OUTFILE  ; output file name
               xor            cx,cx              ; normal file attribute
               int            21h
               pop            si
               jnc            OutputOpenOk
               mov            bl,2
               jmp            ErrVector
OutputOpenOk:  mov            OUTHANDLE,ax       ; save output file handle
               sub            si,EXE_IP          ; ignore IP bytes
               mov            dx,offset LASTBYTE ; buffer location
               mov            cx,32 * 1024       ; buffer size: 32K


; ----------------- Just copy the code from the EXE to the COM file -----------


WriteLoop:     or             si,si
               jz             Done
               cmp            si,cx
               ja             ReadBlock
               mov            cx,si
ReadBlock:     mov            ah,3Fh             ; read from file
               mov            bx,INHANDLE
               int            21h
               jc             ReadError
               mov            ah,40h             ; write to file
               mov            bx,OUTHANDLE
               int            21h
               jc             WriteError
               sub            si,cx
               jmp            WriteLoop

ReadError:     mov            bl,9
               jmp            ErrorExit0
WriteError:    mov            bl,10


;---------------------------- Error exit handler ------------------------------


ErrorExit0:    push           bx
               mov            ah,3Eh             ; close a file
               mov            bx,OUTHANDLE       ; ...output file
               int            21h
               pop            bx

ErrorExit:     mov            EXITCODE,bl
               mov            ah,9               ; display error message
               mov            dx,offset FATALERR ; "Fatal error: "
               int            21h
               mov            si,offset CANTOPEN ; first error message
CheckErr:      dec            bl                 ; is it the error msg we want?
               jz             GotError           ;   yes, got it
FindErr:       lodsb                             ; get a char
               cmp            al,"$"             ; end of message?
               jne            FindErr            ;   no, keep looking
               jmp            CheckErr           ;   see if it's the right one
GotError:      mov            dx,si              ; pointer to error message
ErrDone:       mov            ah,9
               int            21h
               cmp            EXITCODE,1         ; did we open the input file?
               je             Abort              ;   no, don't close or delete

Done:          mov            ah,3Eh             ; close a file
               mov            bx,INHANDLE        ; input file
               int            21h
               cmp            EXITCODE,0         ; successful conversion?
               jnz            Abort              ;   no, just exit
               mov            ah,41h             ; delete file
               mov            dx,offset INFILE   ; ...the original input file
               int            21h
Abort:         mov            ah,4Ch             ; terminate program
               mov            al,EXITCODE        ; error exit code
               int            21h
MAIN           endp


WARNING        db "WARNING: The default IP is not 0100h.",13,10
               db "The output file will not be executable.",13,10,"$"

FATALERR       db "Fatal error: $"
CANTOPEN       db "Unable to open input file",13,10,"$"
CANTMAKE       db "Unable to create output file",13,10,"$"
BADSIG         db "Input file is not a valid .EXE file",13,10,"$"
RELITEMS       db "There are items requiring relocation",13,10,"$"
BADSTACK       db "A stack is defined",13,10,"$"
BADCSEG        db "A code segment is defined",13,10,"$"
BADCOFS        db "The code offset is neither 0 nor 0100h",13,10,"$"
TOOBIG         db "File is too large (.COM would be over 64K)",13,10,"$"
CANTREAD       db "Unable to read input file",13,10,"$"
CANTWRITE      db "Unable to write to output file",13,10,"$"

INFORMATION    db "EXECOM 1.1  Copyright (c) 1988  Thomas G. Hanlin III",13,10
               db "  Purpose: converts an .EXE file to a .COM file.",13,10
               db "  Format : EXECOM inputfile[.EXE] [outputfile[.COM]]",13,10
               db "$"

INHANDLE       dw ?
OUTHANDLE      dw ?
EXE_SIG        dw ?       ; EXE signature
EXE_EXCESS     dw ?       ; bytes in excess of the 512b EXE_PAGES count
EXE_PAGES      dw ?       ; code size, in 512b pages
EXE_RELOCCOUNT dw ?       ; count of items in the relocation table
EXE_HDRSIZE    dw ?       ; header size, in 16b paragraphs
EXE_SS         dw ?       ; default SS
EXE_SP         dw ?       ; default SP
EXE_IP         dw ?       ; default IP
EXE_CS         dw ?       ; default CS

EXITCODE       db ?       ; program error exit code

INFILE         db 80 dup (0)   ; input file name
OUTFILE        db 80 dup (0)   ; output file name

LASTBYTE       db ?       ; final data byte, used as start of file buffer area

Cseg           ends
               end            MAIN
