home *** CD-ROM | disk | FTP | other *** search
- Comment ~
- X2B.ASM
- A replacement for the Exe2Bin program, which is NOT included with
- PC-DOS 3.3.
- written by Henry T. Nettles, Dec. 21, 1987 -- Feb. 20, 1988
- compiled with MicroSoft Macro Assembler, version 5.0
- The inspiration (and model) for this program was EXE2COM.C, written
- by Chris Dunford.
- The include module, DOS.INC, comes with MASM 5.0
- The subroutine BinToStr is borrowed from the SHOW.ASM program which
- also comes with MASM 5.0
- Some of this code (the command line parser) is borrowed from a program
- by Vernon Buerg (TABS.ASM)
- This program is hereby released to the public domain.
-
- Usage: >X2B [d:][\path\]PROG[.EXE] [d:][\path\][PROG][.COM]
-
- The only necessary parameter is the file name of the input file. A
- drive and/or path may be given if needed.
- If no extension is given, .EXE is assumed. If no ouput file is
- given, then the same file name with an extension of .COM is used.
- WARNING: If a drive and/or path is given for the input file, and
- no ouput file is given, then the input drive and/or path will be
- used for the ouput (not the default directory).
-
- This program works on my computer, on the files I have tried it on.
- However, extensive testing has not been performed, and I assume no
- responsibility whatever for the program. I would be interested in
- bug reports, and if anyone improves the code it would be nice to
- receive a copy. I can be reached at:
-
- 22547 Braken Carter
- Katy, Texas 77449-3619
-
- Toad Hall Tweak, 15 Oct 89
- - Converted to .COM format
- - General tightening
- - Moved EXE header buffer, filename buffers to dynamic space at code end
- (to reduce program size)
- - Changed BinToStr procedure so it carries parms directly in AX/DI
- instead of the slow pushes.
- - Removed some unnecessary local variables
-
- Comment ends ~
-
- RECSIZE equ 512
- CMDTAIL equ 80h
- CR equ 0dh
- LF equ 0ah
- EOM equ '$'
- SIZE_OF_HEADER equ 28
-
- exe_header STRUC ; what an exe header looks like
- exe_sig1 db 0 ; EXE file signature: "MZ"
- exe_sig2 db 0
- excess dw 0 ; image size mod 512 (valid bytes in last page)
- pages dw 0 ; # of 512 byte pages in image
- relo_ct dw 0 ; count of relocation table entries
- hdr_size dw 0 ; size of header in paragraphs
- min_mem dw 0 ; min required memory
- max_mem dw 0 ; max required memory
- xss dw 0 ; stack seg offset in load module
- xsp dw 0 ; initial value of sp
- cksum dw 0 ; file checksum
- xip dw 0 ; initial value of IP
- xcs dw 0 ; cs offset in load module
- relo_start dw 0 ; offset of first relocatable item
- ovl_num dw 0 ; overlay number
- exe_header ENDS ; end of structure
-
- CSEG SEGMENT PARA PUBLIC 'CODE'
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- org 100H
-
- X2B proc near
- jmp Start ;skip over runtime data v1.1
-
- errflag db 0 ;error flag v1.1
- save_ip dw 0
- handle1 dw 0
- handle2 dw 0
-
- msg1 db 'Input File ==>',eom
- msg2 db ' Output File ==>',eom
- msg3 db CR,LF,EOM
-
- author db 'X2B 1.1 Public Domain Software by Henry T. Nettles'
- db CR,LF,EOM
- code_size_msg db 'Code Size = '
- code_size_str db ' ',eom
- code_start_msg db ' Code Start = '
- code_start_str db ' ',eom
- ip_msg db ' Initial IP = '
- ip_str db ' ',CR,LF,EOM
- actual_code_size_msg db 'Size of .Com file = '
- actual_code_size_str db ' ',CR,LF,EOM
-
- code_size dw 0 ; amount of code to move to .com file
- code_start dw 0
-
- no_file_msg1 db 'USAGE: >X2B [d:][\path\]file[.exe] [d:][\path\][file][.com]'
- db CR,LF,EOM
- open_fail_msg db 'ERROR: Open of input file failed!',CR,LF,EOM
- create_fail_msg db 'ERROR: Create of output file failed!',CR,LF,EOM
- disk_full_msg db 'ERROR: The disk is full!',CR,LF,EOM
- Bad_Read_msg db 'I/O Error on Read!',CR,LF,EOM
- bad_write_msg db 'I/O Error on Write!',CR,LF,EOM
- seek_error_msg db 'I/O Error on Seek!',CR,LF,EOM
-
- badsig_msg db 'Invalid EXE File Signature',CR,LF,EOM
- hasrelo_msg db 'EXE has relocatable items',CR,LF,EOM
- has_ss_msg db 'EXE has stack segment',CR,LF,EOM
- bad_ip_msg db 'IP not 0 or 100h',CR,LF,EOM
- too_big_msg db 'Exe file is too big to convert to COM file',CR,LF,EOM
- mul_err_msg db 'Overflow on multiply',CR,LF,EOM
-
-
- Start: ;v1.1
-
- ;****************************************************************************
- ;* Get two file names from command line
- ;* This portion of code was borrowed from TABS.ASM by Vernon Buerg
- ;****************************************************************************
-
- xor al,al ;handy 0 v1.1
- mov FNAME1,al ;insure both dynamic filename v1.1
- mov FNAME2,al ; buffers are 0 v1.1
-
- mov si,CMDTAIL ; DS:SI points to command line
- sub bp,bp ;Indicates first or second name
- sub ch,ch ;The PSP may contain one or two
- or cl,[si] ; filenames separated by blanks v1.1
- jz GetF5 ; First byte of cmdline should not be 0
-
- mov di,offset FNAME1 ;ES:DI points to fname1 v1.1
- Inc si ; point to next char from command line
- ; (assume that 1st char is a blank)
-
- GetF1: Lodsb ;Copy command line to file names
- ; AL will contain character pointed
- ; to by DS:SI
- cmp AL,' ' ; skip leading blanks
- jne GetF2 ; not a blank -- jump
-
- or bp,bp ; or until the length is zero
- jz GetF4 ;If a second blank is found,
- mov ax,2400h ; append zero and dollar sign
- stosw ; mov AX to ES:DI
- mov di,offset FNAME2 ;ES:DI now points to 2d filename v1.1
- jmp short GetF4
-
- GetF2: cmp AL,Cr ;Is it CR, end of line?
- je GetF5 ; yes, end of command
-
- stosb ; no, save in name
- mov bp,di ; and indicate data copied
- GetF4: loop GetF1
-
- GetF5: mov ax,2400h ;Append zero and dollar sign
- stosw
-
- mov dx,offset author ;tell them who we are v1.1
- mov ah,9 ;display string
- int 21H
-
- ;***************************************************************************
- ;* DISPLAY THE TWO FILE NAMES FROM COMMAND LINE
- ;***************************************************************************
- mov si,offset FNAME1 ;point to fname1 v1.1
- xor al,al ;handy 0 v1.1
- cmp al,[si] ;did user enter at least 1 file name? v1.1
- jnz L1 ; is ok, we have a file name
- jmp No_File1
-
- L1:
- mov di,si ;offset fname1 ; check fname1 for extension v1.1
- mov cx,-1
- cld ; direction of search = forward
- repnz scasb ; search through file name for '\0'
- not cx ; CX = length including '\0'
- mov dx,cx ;save length in DX a sec v1.1
- mov di,si ;offset fname1 ;point back to fname1 start v1.1
- mov al,'.'
- repnz scasb ; search for period in file name
- jz L1a ; found the period, must have ext
-
- mov ax,si ;offset fname1 ; no period, must add .EXE for user v1.1
- add ax,dx ;fname1_len ; v1.1
- dec ax ;
- mov di,ax ; di points to '\0' at end of file name
- mov ax,'E.' ;452eh ; ".E" backwards v1.1
- stosw
- mov ax,'EX' ;4558h ; "XE" backwards v1.1
- stosw
- mov ax,2400h ; append zero and dollar sign
- stosw
-
- L1a:
- mov cx,-1 ;common code v1.1
- cmp byte ptr FNAME2,0 ;did user enter 2d file name? v1.1
- ; (should not be 0) v1.1
- jnz L2 ; fname2 is non-blank, go check for ext
-
- mov si,offset FNAME1 ;source is FNAME1 v1.1
- mov di,si ;offset FNAME1 ;into DI for a scasb v1.1
- mov al,'.'
- cld
- repnz scasb ; search fname1 for '.'
- not cx ; cx has length of fname1 including '.'
- dec cx ; don't copy the period
- mov di,offset FNAME2 ; let Dest. Index point to fname2 v1.1
- ;SI (source) is already FNAME1 v1.1
- cld ; direction of movement = forward
- rep movsb ; move chars from [SI] to [DI]
- ; CX has count (# chars to move)
- jmp short L2a ; go add the ".COM"
- ;
- L2: mov di,offset fname2 ; check fname2 for extension
- xor al,al ; zero al
- cld ; direction of search = forward
- repnz scasb ; search through file name for '\0'
- not cx ; CX = length including '\0'
- mov dx,cx ; save the length in DX a sec v1.1
- mov di,offset FNAME2 ;v1.1
- mov al,'.'
- repnz scasb ; search for period in file name
- jz L2b ; found the period, must have ext
-
- mov ax,offset FNAME2 ;v1.1
- add ax,dx ;fname2_len ;add in FNAME2's length v1.1
- dec ax ;adjust
- mov di,ax ; di points to '\0' at end of file name
- L2a: mov ax,'C.' ;432eh ; ".C" backwards v1.1
- stosw
- mov ax,'MO' ;4d4fh ;"OM" backwards v1.1
- stosw
- mov ax,2400h ; append zero and dollar sign
- stosw
- L2b:
- mov dx,offset msg1
- mov ah,9
- int 21H
- mov dx,offset FNAME1
- mov ah,9
- int 21H
- mov dx,offset msg2
- mov ah,9
- int 21H
- mov dx,offset FNAME2
- mov ah,9
- int 21H
- mov dx,offset msg3
- mov ah,9
- int 21H
-
- ;****************************************************************************
- ;* OPEN THE INPUT FILE
- ;****************************************************************************
-
- mov dx,offset FNAME1 ;open input file
- mov ax,3D00H ;open, read only
- int 21H
- jnc L3 ; opened ok
- jmp Open_Fail ; no file
-
- L3: mov handle1,ax ; save token for file
-
- ;****************************************************************************
- ;* OPEN THE OUTPUT FILE
- ;****************************************************************************
- mov dx,offset FNAME2 ;output file name
- xor cx,cx ;normal attributes
- mov ah,3CH ;create file
- int 21H
- jnc L4 ; created ok
- jmp Create_Fail ; create failed
-
- L4: mov handle2,ax ; save token for file
-
- ;****************************************************************************
- ;* PROCESS THE EXE HEADER
- ;****************************************************************************
- ; first we read in the exe header, don't you think?
-
- mov dx,offset BUFFER ;read buffer
- mov cx,SIZE_OF_HEADER
- mov bx,handle1
- mov ah,3FH ;read from file/device
- int 21H
- jnc L5 ;read ok
- jmp Bad_Read ;read error
-
- L5: mov bx,offset buffer ; use DS:BX to address buffer
- cmp word ptr [bx].exe_sig1,'ZM' ;'MZ' backwards? v1.1
- je L7 ;YEP, OK
- jmp BadSig ;no, print error msg, exit v1.1
-
- L7:
- xor ax,ax ;handy 0 v1.1
-
- cmp [bx].relo_ct,ax ;is relocatable count 0? v1.1
- je L8 ; is ok, no relocatable items
- jmp HasRelo ; oops, can't convert
-
- L8:
- cmp [bx].xss,ax ;is stack segment 0? v1.1
- je L9 ; is ok, no stack segment
- jmp Has_SS
-
- L9:
- cmp [bx].xsp,ax ;is stack segment offset 0? v1.1
- je L10 ; is okay
- jmp Has_SS
-
- L10:
- mov ax,[bx].xip ; initial value for IP
- or ax,ax ; should either be 0 or 100h v1.1
- je L11
- cmp ax,100h
- je L11
- jmp Bad_IP
-
- L11: mov save_ip,ax ; save the IP for later use
- ;***************************************************************************
- ;* Compute offset of program image in module, and program size
- ;*
- ;* The program size is computed as follows; it cannot exceed 64k bytes
- ;* 512 * (# of 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
- ;*
- ;***************************************************************************
-
- mov ax,[bx].hdr_size ; size of the program header
- ; expressed in 16 byte paragraphs
- mov cl,4 ; no of times to shift
- shl ax,cl ; fast multiply by 16
- mov code_start,ax ; save it
-
- mov ax,[bx].pages ;nr of 512-byte pages v1.1
- dec ax ; subtract 1
- cmp ax,128 ; is it too big? (128*512 is 64k)
- jle L12 ; no, is ok
- jmp Too_Big ; exe file is too big, print error
-
- L12: mov cl,9 ; number of times to shift left
- shl ax,cl ; fast multiply by 512
- jno L13 ; jump on no overflow
- jmp Mul_Err ; else multiply error
-
- L13:
- mov cx,[bx].excess ; no. of bytes in last non-full page
- add ax,cx ; add to result from above
- sub ax,code_start ; subtract the code start address
- mov code_size,ax ; save it for later
- mov di,offset code_size_str ;where to write the Ascii chars v1.1
- call BinToStr ;convert code size v1.1
- mov dx,offset code_size_msg ;'Code size = xxxx' v1.1
- mov ah,9 ;display msg
- int 21H
-
- mov ax,code_start ;convert code start address v1.1
- mov di,offset code_start_str ;where to write the Ascii chars v1.1
- call BinToStr
- mov dx,offset code_start_msg ;'Code start = xxxx' v1.1
- mov ah,9
- int 21H
-
- mov ax,save_ip ;program's IP
- mov di,offset ip_str ;where to write the Ascii chars v1.1
- call BinToStr
- mov dx,offset ip_msg ;'Initial IP = xxxx' v1.1
- mov ah,9
- int 21H
-
- ;****************************************************************************
- ;* MOVE FILE POINTER TO START OF CODE
- ;****************************************************************************
- ;* Add the initial IP to the code start address. This will give
- ;* us the file offset, which is the location in the EXE file that we
- ;* will start copying from
- ;*
- mov ax,code_start
- add ax,save_ip ;add in program's IP v1.1
- mov dx,ax ;DX needs it as lower part v1.1
- ;of file offset v1.1
-
- mov bx,handle1 ; handle of input file
- xor cx,cx ; upper part of offset v1.1
- mov ax,4200h ; move file pointer v1.1
- ;(from start)
- int 21h
- jnc L14
- jmp Seek_Error
-
- L14:
- ;****************************************************************************
- ;* COPY THE CODE TO THE OUTPUT (.COM) FILE
- ;****************************************************************************
- ; reduce the code_size by the size of the IP
- mov ax,code_size ; reduce the code_size
- sub ax,save_ip ; by the IP size v1.1
- mov code_size,ax ; store it away
-
- mov di,offset actual_code_size_str ;v1.1
- call BinToStr ;convert code size
-
- mov dx,offset actual_code_size_msg ;'Size of .COM file = xxxx' v1.1
- mov ah,9
- int 21H
- Next: ; process next record
- mov ax,RECSIZE
- cmp ax,code_size ; compare code_size to RECSIZE
- jle L15 ; if ax < code_size, use ax as is
- mov ax,code_size ; else use size of remaining code
- L15: mov cx,ax ; # of bytes to read
- mov bx,handle1 ; input file
- mov dx,offset buffer ; where to put it
- mov ax,3f00h ; read from file/device
- int 21h
- jnc L16 ; read ok
- jmp Bad_Read ; ERROR - get out of here
-
- L16: or ax,ax ; on return, AX has # of bytes read
- jnz L17 ; if not zero, keep on
- jmp Done ; read zero bytes, must be done
-
- L17:
- mov cx,ax ; nr bytes to write v1.1
- mov bx,handle2 ; output file
- mov dx,offset buffer ; addr of what we are about to write
- mov ah,40h ; write to file/dev
- int 21h
- jnc L18 ; carry flag not set, no error on write
- jmp Bad_Write ; jump if write error
-
- L18: cmp ax,cx ;wsize ; AX has # of bytes actually written
- je L19 ; if we wrote all of the bytes
- ; that we wanted to, then keep on
- jmp Disk_Full ; did NOT write all of the bytes
- ; that I wanted to, disk must be full
- L19: mov bx,code_size
- xchg ax,bx ; ax has code_size, bx has bytes written
- sub ax,bx ; subtract bytes written from code_size
- ; which gives us the number of
- mov code_size,ax ; bytes remaining to be copied
- or ax,ax ; bytes remaining = zero? v1.1
- je Done ; yes, we're finished
- jmp Next ; no, go read next block
-
- ;****************************************************************************
- ;* ERROR MESSAGES
- ;****************************************************************************
-
- No_File1: mov dx,offset no_file_msg1
- jmp short Print_Error
- Open_Fail: mov dx,offset open_fail_msg
- jmp short Print_Error
- Create_Fail: mov dx,offset create_fail_msg
- jmp short Print_Error
- Disk_Full: mov dx,offset disk_full_msg
- jmp short Print_Error
- Bad_Read: mov dx,offset bad_read_msg
- jmp short Print_Error
- Bad_Write: mov dx,offset bad_write_msg
- jmp short Print_Error
- Seek_Error: mov dx,offset seek_error_msg
- jmp short Print_Error
- BadSig: mov dx,offset badsig_msg
- jmp short Print_Error
- HasRelo: mov dx,offset hasrelo_msg
- jmp short Print_Error
- Has_SS: mov dx,offset has_ss_msg
- jmp short Print_Error
- Bad_IP: mov dx,offset bad_ip_msg
- jmp short Print_Error
- Too_Big: mov dx,offset too_big_msg
- jmp short Print_Error
- Mul_Err: mov dx,offset mul_err_msg
-
- Print_Error: mov ah,9
- int 21h
- not errflag ;turn error flag on v1.1
- ;(non-zero)
-
- ;****************************************************************************
- ;* CLOSE INPUT AND OUTPUT FILES
- ;****************************************************************************
-
- Done: mov ax,handle1 ;input file handle
- or ax,ax ; is the handle still zero,
- ; as was initially? v1.1
- je Get_Out ; yes, no files to close
- mov ah,3eh ; close input file
- mov bx,handle1
- int 21h
-
- mov ax,handle2 ;output file handle
- or ax,ax ; is the handle still zero,
- ; as was initially? v1.1
- je Get_Out ; yes, no output file to close
- mov ah,3eh ; close output file
- mov bx,handle2
- int 21h
-
- cmp errflag,0 ;any errors? v1.1
- jz Get_Out ; no
- mov dx,offset FNAME2 ; yes, delete the output file v1.1
- mov ah,41H ;delete file
- int 21H
-
- ;****************************************************************************
- ;* EXIT WITH STATUS CODE
- ;****************************************************************************
-
- Get_Out:
- mov ax,4C00H ;terminate, errorlevel 0 v1.1
- int 21H
-
- X2B endp ;v1.1
-
- ; Procedure BinToStr (number,address)
- ; Purpose Converts integer to string
- ; Input ax = number to convert, di = near address for write v1.1
- ; Output AX has characters written
-
- BinToStr PROC near
-
- sub cx,cx ; Clear counter
- mov bx,10 ; Divide by 10
-
- ; Convert and save on stack backwards
-
- GetDigit:
- sub dx,dx ; Clear top
- div bx ; Divide to get last digit as remainder
- add dl,"0" ; Convert to ASCII
- push dx ; Save on stack
- or ax,ax ; Quotient 0?
- loopnz GetDigit ; No? Get another
-
- ; Take off the stack and store forward
-
- neg cx ; Negate and save count
- mov dx,cx
- PutDigit:
- pop ax ; Get character
- stosb ; Store it
- loop PutDigit
- mov ax,dx ; Return digit count
-
- ret ; v1.1
- BinToStr ENDP
-
- even
-
- buffer label byte ;recsize dup(?)
- FNAME1 equ buffer + RECSIZE ;64-byte input filename buffer
- FNAME2 equ FNAME1 + 64 ;64-byte output filename buffer
-
- CSEG ENDS
- END X2B