Category : Assembly Language Source Code
Archive   : X2B11.ZIP
Filename : X2B11.ASM

Output of file : X2B11.ASM contained in archive : X2B11.ZIP
Comment ~
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 '$'

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


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'
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]'
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

mov dx,offset author ;tell them who we are v1.1
mov ah,9 ;display string
int 21H

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

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
mov ax,'EX' ;4558h ; "XE" backwards v1.1
mov ax,2400h ; append zero and dollar sign

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,'.'
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
mov ax,'MO' ;4d4fh ;"OM" backwards v1.1
mov ax,2400h ; append zero and dollar sign
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


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

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

; first we read in the exe header, don't you think?

mov dx,offset BUFFER ;read buffer
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

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

cmp [bx].xss,ax ;is stack segment 0? v1.1
je L9 ; is ok, no stack segment
jmp Has_SS

cmp [bx].xsp,ax ;is stack segment offset 0? v1.1
je L10 ; is okay
jmp Has_SS

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

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

;* 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

; 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

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


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


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


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

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
pop ax ; Get character
stosb ; Store it
loop PutDigit
mov ax,dx ; Return digit count

ret ; v1.1


buffer label byte ;recsize dup(?)
FNAME1 equ buffer + RECSIZE ;64-byte input filename buffer
FNAME2 equ FNAME1 + 64 ;64-byte output filename buffer


  3 Responses to “Category : Assembly Language Source Code
Archive   : X2B11.ZIP
Filename : X2B11.ASM

  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: