;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

Title	E2BINIT(EXE2BIN)

;*****************************************************************************
;   Loader for EXE files under 86-DOS
;   VER 1.5
;	05/21/82   Added rev number
;   VER 1.6
;	07/01/82   A little less choosy about size matches
;   VER 2.0  M.A.U
;	10/08/82   Modified to use new 2.0 system calls for file i/o
;   Ver 2.1  M.A.U
;	10/27/82   Added the DOS version check
;   Ver 2.2  MZ
;	8/30/83     Fixed command line parsing
;   Ver 2.3  EE
;	10-12-83    More fixes to command line parsing
;   Ver 2.4  NP
;	10/17/83    Use Printf for messages
;   Ver 2.5  MZ     Fix LOCATE sss D: problem
;	04/09/87    Add PARSER and MESSAGE RETRIEVER
;   Ver 4.00  DRM
;
;       M001    MD      12/27/90        Removed special checks for ambiguous
;                                       file names
;*****************************************************************************


INCLUDE SYSMSG.INC
MSG_UTILNAME <EXE2BIN>							;AN000;

	subttl	Main Code Area						;AN000;
	page


; The following switch allows use with the "old linker", which put a version
; number where the new linker puts the number of bytes used in the last page.
; If enabled, this will cause a test for 0004 at this location (the old linker
; version number), and if equal, change it to 200H so all of the last page
; will be used.

OLDLINK EQU	0			;1 to enable, 0 to disable

CODE	SEGMENT PARA PUBLIC 'CODE'					;AN000;
CODE	ENDS								;AN000;
DATA	SEGMENT PARA PUBLIC 'DATA'					;AN000;
DATA	ENDS								;AN000;
STACK	SEGMENT PARA PUBLIC 'STACK'					;AN000;
STACK	ENDS								;AN000;
ZLOAD	SEGMENT PARA PUBLIC 'ZLOAD'					;AN000;
ZLOAD	ENDS								;AN000;

DATA	SEGMENT PARA PUBLIC 'DATA'					;AN000;

MSG_SERVICES <MSGDATA>							;AN000;

Command_Line_Buffer db 128 dup(0)					;AN000;
Command_Line_Length equ $ - Command_Line_Buffer 			;AN000;

Fatal_Error db	0							;AN000;

Command_Line db NO



rev	db	"2.4"


file1_ext db	".EXE",00h
file2_ext db	".BIN",00h

per11	db	0							;AN000;
per2	db	0
per22	db	0							;AN000;

update	equ	0							;AN000;
noupdate equ	-1							;AN000;

file1	db	(64+13) dup(?)
fnptr	dw	offset file1		; Ptr to filename in file1
handle1 dw	1 dup(?)

file2	db	(64+13) dup(?)
f2cspot dw	offset file2		; Ptr to spot in file2, file1 maybe added

name2_given	db	1		;1-> atleast the file2 name is present
ext2_given	db	0		;1-> full file2 spec given
file2_given	db	0		; is TRUE if user specified atleast a 
					; drive, or path (or full name)
handle2 dw	1 dup(?)

dma_buf db	80h dup(0)		; DMA transfer buffer

INBUF	DB	5,0
	DB	5 DUP(?)

;The following locations must be defined for storing the header:

RUNVAR	LABEL	BYTE			;Start of RUN variables
RELPT	DW	?
LASTP	LABEL	WORD
RELSEG	DW	?
SIZ	LABEL	WORD			;Share these locations
PAGES	DW	?
RELCNT	DW	?
HEADSIZ DW	?
	DW	?
LOADLOW DW	?
INITSS	DW	?
INITSP	DW	?
	DW	?
INITIP	DW	?
INITCS	DW	?
RELTAB	DW	?
RUNVARSIZ EQU	$-RUNVAR

DBCS_Vector_Off dw 0							;AN000;
DBCS_Vector_Seg dw 0							;AN000;

parse_ptr DW	?

DATA	ENDS


STACK	SEGMENT PARA PUBLIC 'STACK'
	DB	(362 - 80h) + 80H DUP (?) ; (362 - 80h) is IBMs ROM requirement
					; (New - Old) == size of growth
STACK	ENDS
;



ZLOAD	SEGMENT PARA PUBLIC 'ZLOAD'
	db	?
ZLOAD	ENDS
LOAD	EQU	ZLOAD
;



;
;*****************************************************************************
; Include files
;*****************************************************************************
;

.xlist
INCLUDE DOSSYM.INC			; also versiona.inc		;AN000;
INCLUDE SYSCALL.INC							;AN000;
INCLUDE E2BMACRO.INC							;AN000;
INCLUDE E2BEQU.INC							;AN000;
INCLUDE E2BTABLE.INC							;AN000;
INCLUDE E2BPARSE.INC							;AN000;
include version.inc
.list



CODE	SEGMENT PARA PUBLIC 'CODE'
	assume	cs:CODE,ds:DATA,es:NOTHING,SS:STACK			;AN000;

psp_ptr dw	1 dup(?)						;AN000;
;
;*****************************************************************************
; SysDisplayMsg Declarations
;*****************************************************************************
;
.xlist
MSG_SERVICES <LOADmsg>							;AN000;
MSG_SERVICES <DISPLAYmsg,CHARmsg>					;AN000;
MSG_SERVICES <EXE2BIN.CLA,EXE2BIN.CLB>					;AN000;
MSG_SERVICES <EXE2BIN.CL1,EXE2BIN.CL2>					;AN000;
MSG_SERVICES <EXE2BIN.CTL>						;AN000;


.list

;
;*****************************************************************************
; External Routine Declarations
;*****************************************************************************
;

	public	SysDispMsg						;AN000;
	public	SysLoadMsg						;AN000;


;*****************************************************************************
;Routine name:	Main_Init
;*****************************************************************************
;
;Description: Main control routine for init section
;
;Called Procedures: Message (macro)
;		    Check_DOS_Version
;		    Init_Input_Output
;		    Validate_Target_Drive
;		    Hook_CNTRL_C
;
;Input: None
;
;Output: None
;
;Change History: Created	6/22/87 	DM
;
;*****************************************************************************

procedure Main_Init near		;				;AN000;

	ASSUME	DS:NOTHING		; THIS IS WHAT dos GIVES YOU	;AN000;
	ASSUME	ES:NOTHING						;AN000;

	PUSH	DS							;AN000;
	mov	psp_ptr,ds						;AN000;
	XOR	AX,AX							;AN000;
	PUSH	AX			;Push return address to DS:0	;AN000;

	MOV	AX,SEG DATA		;SET UP ADDRESSABILITY TO	;AN000;
	MOV	DS,AX			; THE DATA SEGMENT		;AN000;
	ASSUME	DS:DATA 		;TELL ASSEMBLER WHAT I JUST DID ;AN000;

	mov	Fatal_Error,No		;Init the error flag		;AN000;
	call	Init_Input_Output	;Setup messages and parse	;AN000;
	cmp	Fatal_Error,Yes 	;Error occur?			;AN000;
;	$IF	NE			;Nope, keep going		;AN000;
	JE $$IF1
	    call    LOCATE		;Go do the real program 	;AN000;
;	$ENDIF								;AN000;
$$IF1:
	xor	al,al							;AN000;
	Dos_call Exit							;AN000;
	int	20h			;If other exit fails		;AN000;

Main_Init endp								;AN000;

;*****************************************************************************
;Routine name: Init_Input_Output
;*****************************************************************************
;
;Description: Initialize messages, Parse command line, allocate memory as
;	      needed. If there is a /FS switch, go handle it first as
;	      syntax of IFS format may be different from FAT format.
;
;Called Procedures: Preload_Messages
;		    Parse_For_FS_Switch
;		    Parse_Command_Line
;		    Interpret_Parse
;
;Change History: Created	6/22/87 	DM
;
;Input: PSP command line at 81h and length at 80h
;	Fatal_Error  = No
;
;Output: Fatal_Error = YES/NO
;
;*****************************************************************************

procedure Init_Input_Output near					;AN000;

	call	Preload_Messages	;Load up message retriever	;AN000;
	cmp	Fatal_Error,YES 	;Quit?				;AN000;
;	$IF	NE			;Nope, keep going		;AN000;
	JE $$IF3
	    call    Parse_Command_Line	;Parse in command line input	;AN000;
;	$ENDIF								;AN000;
$$IF3:
	ret								;AN000;

Init_Input_Output endp							;AN000;

;*****************************************************************************
;Routine name: Preload_Messages
;*****************************************************************************
;
;Description: Preload messages using common message retriever routines.
;
;Called Procedures: SysLoadMsg
;
;
;Change History: Created	6/22/87 	DM
;
;Input: Fatal_Error = NO
;
;Output: Fatal_Error = YES/NO
;
;*****************************************************************************

procedure Preload_Messages near 					;AN000;

	call	SYSLOADMSG		;Preload the messages		;AN000;
;	$IF	C			;Error? 			;AN000;
	JNC $$IF5
	    call    SYSDISPMSG						;AN000;
	    mov     fatal_error, YES					;AN000;
;	$ENDIF								;AN000;
$$IF5:
	ret								;AN000;
Preload_Messages endp							;AN000;


;*****************************************************************************
;Routine name: Parse_Command_Line
;*****************************************************************************
;
;Description: Parses command line.
;
;Called Procedures: Message (macro)
;		    Sysparse
;
;Change History: Created	6/22/87 	DM
;
;Input: Fatal_Error = NO
;
;Output: Fatal_Error = YES/NO
;
;*****************************************************************************


Procedure Parse_Command_Line						;AN000;

	push	ds							;AN000;
	mov	ds,psp_ptr						;AN000;
	ASSUME	DS:NOTHING						;AN000;
	mov	si,Command_Line_Parms					;AN000;
	mov	ax,seg command_line_table				;AN000;
	push	es							;AN000;
	mov	es,ax							;AN000;
	ASSUME	ES:NOTHING						;AN000;
	mov	di,offset Command_Line_Table				;AN000;
	xor	cx,cx							;AN000;

	PUBLIC	MainParseLoop
MainParseLoop:

;	$DO								;AN000;
$$DO7:
	    xor     dx,dx						;AN000;
	    mov     es:parse_ptr,si
	    call    Sysparse						;AN000;
	    cmp     ax,No_Error 					;AN000;

;	    $IF     E							;AN000;
	    JNE $$IF8

	        ; Check if /? switch entered.
		; If so, display the options help message
		; and set for exit.
		;
		; This gives the user the info they want,
		; without all the other possible error messages.
		;
		; 4/18/90 c-PaulB

		cmp	es:[sw_synonym1], offset sw1_s1	; /?
		jne	CheckSW1Done			;  skip this if not
		call	DisplayOptions			; else display msg
		mov	es:[Fatal_Error], YES		; set flag to stop
		jmp	ParseCLExit			;  and bail out now
CheckSW1Done:

		push	ax						;AN000;
		push	bx						;AN000;
		push	ds						;AN000;
		push	es						;AN000;
		push	si						;AN000;
		push	di						;AN000;

		cmp	cx,1						;AN000;

;		$IF	E						;AN000;
		JNE $$IF9

		    mov     ax,seg rb_string1_off			;AN000;
		    mov     ds,ax					;AN000;
		    ASSUME  DS:NOTHING					;AN000;
		    mov     si,offset rb_string1_off			;AN000;
		    mov     ax,ds:[si]					;AN000;
		    mov     bx,ax					;AN000;


		    mov     ax,ds:[si+2]				;AN000;
		    mov     ds,ax					;AN000;
		    ASSUME  DS:NOTHING					;AN000;
		    mov     si,bx					;AN000;

		    mov     ax,seg file1				;AN000;
		    mov     es,ax					;AN000;
		    ASSUME  ES:NOTHING					;AN000;
		    mov     di,offset file1				;AN000;
		    call    copyfs					;AN000;

;		$ELSE							;AN000;
		JMP SHORT $$EN9
$$IF9:

		    mov     ax,seg rb_string2_off			;AN000;
		    mov     ds,ax					;AN000;
		    ASSUME  DS:NOTHING					;AN000;
		    mov     si,offset rb_string2_off			;AN000;
		    mov     ax,ds:[si]					;AN000;
		    mov     bx,ax					;AN000;


		    mov     ax,ds:[si+2]				;AN000;
		    mov     ds,ax					;AN000;
		    ASSUME  DS:NOTHING					;AN000;
		    mov     si,bx					;AN000;

		    mov     ax,seg file2				;AN000;
		    mov     es,ax					;AN000;
		    ASSUME  ES:NOTHING					;AN000;
		    mov     di,offset file2				;AN000;
		    call    copyfs					;AN000;

;		$ENDIF							;AN000;
$$EN9:

		pop	di						;AN000;
		pop	si						;AN000;
		pop	es						;AN000;
		ASSUME	ES:NOTHING					;AN000;
		pop	ds						;AN000;
		ASSUME	DS:NOTHING					;AN000;
		pop	bx						;AN000;
		pop	ax						;AN000;

;	    $ENDIF							;AN000;
$$IF8:

	    cmp     ax,No_Error 					;AN000;

;	$ENDDO	NE							;AN000;
	JE $$DO7

	cmp	ax,End_of_Parse 	;Check for parse error		;AN000;
;	$IF	NE							;AN000;
	JE $$IF14
		push	ax						;AN001;
		mov	ax,es:parse_ptr 				;AN001;
		mov	es:parsoff,ax					;AN001;
		mov	es:parseg,ds					;AN001;
		mov	byte ptr ds:[si],0				;AN001;
		pop	ax						;AN001;
		parse_message		       ;Must enter file name	;AN000;
		mov	es:Fatal_Error,YES     ;Indicate death! 	;AN000;
;	$ENDIF								;AN000;
$$IF14:
ParseCLExit:
	pop	es							;AN000;
	ASSUME	ES:NOTHING						;AN000;
	pop	ds							;AN000;
	ASSUME	DS:DATA 						;AN000;

	ret								;AN000;

Parse_Command_Line endp 						;AN000;

;*****************************************************************************
;Routine name: Parse_Command_Line
;*****************************************************************************
;
;Description:       Displays options help message lines.
;
;Called Procedures: Display_Interface
;
;Change History:    Created 5/2/90 c-PaulB
;
;Input:             No value passed.
;
;Output:            No value returned.
;
;*****************************************************************************


Procedure DisplayOptions

	mov     dx, offset msgOptions		; get options msg
DO_Loop:
	call    Display_Interface		;  and show it
	cmp	word ptr es:[msgOptions], MSG_OPTIONS_LAST	; last msg?
	je	DO_Done				; done if so
	inc	word ptr es:[msgOptions]		; else get next msg
	jmp	short DO_Loop			;  and go do it
DO_Done:
	ret

DisplayOptions	endp

;*****************************************************************************

INCLUDE PARSE.ASM

;*****************************************************************************


procedure LOCATE near

	push	ds							;AN000;
	ASSUME	ES:NOTHING		; THIS IS THE WAY IT GETS HERE! ;AN000;
	mov	ax,es			; ES -> PSP			;AN000;
	mov	ds,ax			; DS -> PSP			;AN000;
	ASSUME	DS:NOTHING						;AN000;

	MOV	SI,offset file1
	MOV	BX,SEG DATA
	MOV	ES,BX
	assume	es:data 						;AN000;

	MOV	BX,WORD PTR DS:[2]	;Get size of memory


;-----------------------------------------------------------------------;

;
; The rules for the arguments are:
;   File 1:
;	If no extention is present, .EXE is used.
;   File 2:
;	If no drive is present in file2, use the one from file1
;	If no path is specified, then use current dir
;	If no filename is specified, use the filename from file1
;	If no extention is present in file2, .BIN is used
;


;----- Get the first file name
	push	ds							;AN000;
	push	es							;AN000;
	ASSUME	ES:DATA 						;AN000;
	pop	ds							;AN000;
	ASSUME	DS:DATA 						;AN000;

sj01:
	mov	si,offset file1 	;   d = file1;
	mov	per11,0 		;   assume no extension on file1;AC000;

;******************************************************************************

sj0:
	lodsb				;   while (!IsBlank(c=*p++)) {
	cmp	al,0
	JE	SJ2
	call	dbcs_check		; see if a dbcs character	;AN000;
	jc	dbcs_1			; dbcs character, go load another char ;AN000;
	cmp	al,'\'			;	if (c == '\\' || c == ':') {
	jnz	sj05
	mov	per11,update						;AC000;
	mov	fnptr,si		;	    fnptr = ptr to slash
sj05:
	cmp	al,':'			;	if (c == '\\' || c == ':') {
	jnz	checkper1
	mov	per11,update						;AC000;
	mov	fnptr,si		;	    fnptr = ptr to slash
checkper1:
	cmp	al,'.'			;	if (c == '.')
	jne	sj0                     ;                               ;M001
	mov	per11,noupdate		;   set file1 to have extension ;AN000;
        jmp     short sj0               ;                               ;M001
                                        ;M001 - code removed
dbcs_1: 				;				;AN000;
	lodsb				; load another character and got to ;AN000;
	jmp	short sj0		; the start again.		;AN000;

;******************************************************************************

sj2:
get_second:
;----- Get the second file name
; Initially we assume that user has not given any file2 spec. Then if we find
; that user has entered something, then file2_given is made to TRUE;
; Once file2_given is TRUE, then we assume that user has given atleast a name
; i.e. name2_given = 1 but ext2_given = 0;
; if we find that user has given just a drive: then name2_given is made to FALSE
; The logic then simplifies to :
;	if (!file2_given) 
;		copy file1 and add .BIN ext and try to open
; 	else { /* file2 given */
;		if (!name2_given) 
;			copy name from file1 and .BIN as extn ; try to open
;		else { /* name2 also given */
;			do find_first		
;			if (file_not_found) 
;				add extn .BIN if needed and try to open
;			else if (it is subdir)  {
;				add '\' to it;
;				add the file1 name and .BIN 
;				go for file open
;			}	
;		} /* else name2 given*/
;	} /* else file2 given */

	MOV	SI,offset file1
	mov	di,offset file2 	;   d = file2

;******************************************************************************

sj3:
	cmp	word ptr [di],00	;   check to see if first character of
	je	sj32			;   file2 is a null.		;AN000;
	mov	file2_given,1		;   user has given atleast a partial
					;    spec for file2
	mov	si,offset file2 	;   set pointer to file2

;******************************************************************************

sj31:
	lodsb				;   If file2 first character is not a
	mov	f2cspot,si
	cmp	al,0			;   null, this loop will check to see
	JZ	maycopy 		;   the file has an extension assigned;AN000;
	call	dbcs_check		; to it.  If not it will set per2 to  ;AN000;
	jc	dbcs_2			; go load another byte		      ;AN000;
	cmp	al,'\'			
	jnz	checkper6	
	cmp	byte ptr [si],0		;   end of file2 spec ?
	jne	sj31			;  no, go get next char
	mov	name2_given,0		; copy from file1
checkper6:
	cmp	al,':'			;	if (c == '\\' || c == ':') {
	jnz	checkper4
	cmp	byte ptr [si],0		; end of file2 spec ?
	jne	sj31			; no, go get next char
	mov	name2_given,0		; no name; copy from file1
checkper4:				;   there is an extension already.
	cmp	al,'.'			;
	jne	sj31			;       M001
	mov	ext2_given,1		;       M001 - code removed
	jmp	short sj31		;       M001
                                        ;       M001 - code removed
dbcs_2:                                 ;
	lodsb				;load another character and got to    ;AN000;
	jmp	short sj31		;the start again.		      ;AN000;

;******************************************************************************

; we get here only if user specified something - a drive, just a name or
; even the full spec
; check if we have to copy the name 

maycopy:				
	cmp	name2_given,1		; did the user give a name
	je	sj5			; yes, go check for the existence 
	
	dec	f2cspot 						      
	mov	di,f2cspot						     

sj32:
					;   There is no second filename so
	mov	si,fnptr						      ;AN000;

;******************************************************************************

copy1to2:								      ;AN000;
	lodsb				; This loop is executed when there is ;AN000;
	cmp	al,0			; no file2 specified on the command   ;AN000;
	JZ	SJ5			; line.  It will copy the file1 name  ;AN000;
	call	dbcs_check		; check for dbcs character	      ;AN000;
	jc	dbcs_3			; got a dbcs character, go copy.      ;AN000;
	cmp	al,'.'			; extension.  The defult extension    ;AN000;
	je	sj5			; of .BIN will be added in check_ext. ;AN000;
	stosb								      ;AN000;
	jmp	short copy1to2						      ;AN000;
dbcs_3:
	stosb				; Got a dbcs character. Copy	      ;AN000;
	lodsb				; two characters and then go to       ;AN000;
	stosb				; next character in filename.	      ;AN000;
	jmp	short copy1to2						      ;AN000;	     ;AN000;

;******************************************************************************

sj5:
;	mov	byte ptr es:[di],00h	;   *d = 0;
	cmp	file2_given,1		; if the user specified some path
	jne	check_ext		; we need to check it; else 
	cmp	name2_given,1		; add .BIN and try to open file2
	jne	check_ext		; (at this point we have the name)
	mov	ah,Set_DMA		; Use find_first to see if file2 is
	mov	dx,offset dma_buf	; a directory.	If it isn't, go to
	int	21h			; set f2cspot to point to the spot
	mov	ah,Find_First		; right after the backslash, and
	mov	dx,offset file2 	; fall through to no_second so that
	mov	cx,-1			; file1's name will be added to file2.
	int	21h
	jc	check_ext
	test	dma_buf+21,00010000b
	jNZ	DoDirectory
	jmp	short Check_Ext
DoDirectory:
	mov	AL,'\'
	mov	di,f2cspot
	dec	di
	stosb
	inc	f2cspot
	mov	name2_given,0		; so that we will copy file1.ext to
	mov	ext2_given,0		; file2; we also have to copy ext
	jmp	maycopy			; 


;----- Check that files have an extension, otherwise set default
check_ext:
	cmp	per11,noupdate		;   if (ext2_given == NULL) { 	;AC000;
	jz	file1_ok
	mov	di,offset file1 	;	d = file1;
	mov	si,offset file1_ext	;	s = ".EXE";
	call	strcat			;	strcat (d, s);
file1_ok:				;	}
	cmp	ext2_given,1		;   if (ext2_given == NULL) { 	;AC000;
	je	file2_ok
	mov	di,offset file2 	;	d = file2;
	mov	si,offset file2_ext	;	s = ".BIN";
	call	strcat			;	strcat (d, s);
	jmp	short file2_ok		;	}

;-----------------------------------------------------------------------;
file2_ok:
	mov	dx,offset file1
	mov	ax,(open SHL 8) + 0	;for reading only
	INT	21H			;Open input file
	jc	bad_file
	mov	[handle1],ax
	jmp	short exeload

bad_file:
	jmp	DosError

BADEXE:
	pop	ds
	ASSUME	DS:nothing						;AN000;
	MESSAGE msgNoConvert						;AC000;
	jmp	getout							;AN000;

ReadError:
	jmp	DosError

EXELOAD:
	ASSUME	DS:DATA 						;AN000;
	MOV	DX,OFFSET RUNVAR	;Read header in here
	MOV	CX,RUNVARSIZ		;Amount of header info we need
	push	bx
	mov	bx,[handle1]
	MOV	AH,read
	INT	21H			;Read in header
	pop	bx
	jc	ReadError
	CMP	[RELPT],5A4DH		;Check signature word
	JNZ	BADEXE
	MOV	AX,[HEADSIZ]		;size of header in paragraphs
	ADD	AX,31			;Round up first
	CMP	AX,1000H		;Must not be >=64K
	JAE	TOOBIG
	AND	AX,NOT 31
	MOV	CL,4
	SHL	AX,CL			;Header size in bytes

	push	dx
	push	cx
	push	ax
	push	bx
	mov	dx,ax
	xor	cx,cx
	mov	al,0
	mov	bx,[handle1]
	mov	ah,lseek
	int	21h
	jc	LseekError
	pop	bx
	pop	ax
	pop	cx
	pop	dx

	XCHG	AL,AH
	SHR	AX,1			;Convert to pages
	MOV	DX,[PAGES]		;Total size of file in 512-byte pages
	SUB	DX,AX			;Size of program in pages
	CMP	DX,80H			;Fit in 64K? (128 * 512 = 64k)
	JAE	TOOBIG
	XCHG	DH,DL
	SHL	DX,1			;Convert pages to bytes
	MOV	AX,[LASTP]		;Get count of bytes in last page
	OR	AX,AX			;If zero, use all of last page
	JZ	WHOLEP

	IF	OLDLINK
	    CMP     AX,4		;Produced by old linker?
	    JZ	    WHOLEP		;If so, use all of last page too
	ENDIF

	SUB	DX,200H 		;Subtract last page
	ADD	DX,AX			;Add in byte count for last page
WHOLEP:
	MOV	[SIZ],DX
	ADD	DX,15
	SHR	DX,CL			;Convert bytes to paragraphs
	MOV	BP,SEG LOAD
	ADD	DX,BP			;Size + start = minimum memory (paragr.)
	CMP	DX,BX			;Enough memory?
	JA	TOOBIG
	MOV	AX,[INITSS]
	OR	AX,[INITSP]
	OR	AX,[INITCS]
	JMP	short ERRORNZ

TOOBIG:
	pop	ds
	ASSUME	DS:NOTHING						;AN000;
	MESSAGE msgOutOfMemory						;AN000;
	jmp	getout							;AN000;

LseekError:
	jmp	DosError


ERRORNZ:
	ASSUME	DS:DATA 						;AN000;
	jz	xj
	JMP	BADEXE			;AC000; For ptm P475;
xj:	MOV	AX,[INITIP]
	OR	AX,AX			;If IP=0, do binary fix
	JZ	BINFIX
	CMP	AX,100H 		;COM file must be set up for CS:100
	JNZ	ERRORNZ

	push	dx
	push	cx
	push	ax
	push	bx
	mov	dx,100h 		;chop off first 100h
	xor	cx,cx
	mov	al,1			;seek from current position
	mov	bx,[handle1]
	mov	ah,lseek
	int	21h
	jc	LseekError
	pop	bx
	pop	ax
	pop	cx
	pop	dx

	SUB	[SIZ],AX		;And count decreased size
	CMP	[RELCNT],0		;Must have no fixups
	JNZ	ERRORNZ
BINFIX:
	XOR	BX,BX			;Initialize fixup segment
;See if segment fixups needed
	CMP	[RELCNT],0
	JZ	LOADEXE
GETSEG:
	pop	ds
	ASSUME	DS:NOTHING						;AN000;
	MESSAGE msgFixUp						;AN000;
	PUSH	DS
	PUSH	ES
	POP	DS
	ASSUME	DS:DATA 						;AN000;
	MOV	AH,STD_CON_STRING_INPUT
	MOV	DX,OFFSET INBUF
	INT	21H			;Get user response
	MOV	SI,OFFSET INBUF+2
;;dcl;; MOV	BYTE PTR [SI-1],0	;Any digits?
	cmp	BYTE PTR [SI-1],0	;Any digits?			;AC000;
	JZ	GETSEG
DIGLP:
	LODSB
	SUB	AL,"0"
	JC	DIGERR
	CMP	AL,10
	JB	HAVDIG
	AND	AL,5FH			;Convert to upper case
	SUB	AL,7
	CMP	AL,10
	JB	DIGERR
	CMP	AL,10H
	JAE	DIGERR
HAVDIG:
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	OR	BL,AL
	JMP	DIGLP

DIGERR:
	CMP	BYTE PTR [SI-1],0DH	;Is last char. a CR?
	JNZ	GETSEG
LOADEXE:
	XCHG	BX,BP			;BX has LOAD, BP has fixup

	MOV	CX,[SIZ]
	MOV	AH,read
	push	di
	mov	di,[handle1]
	PUSH	DS
	MOV	DS,BX
	ASSUME	DS:NOTHING						;AN000;
	XOR	DX,DX
	push	bx
	mov	bx,di
	INT	21H			;Read in up to 64K
	pop	bx
	POP	DS
	ASSUME	DS:DATA 						;AN000;
	pop	di
	Jnc	HAVEXE			;Did we get it all?

	jmp	DosError

LseekError2:
	jmp	DosError

HAVEXE:
	ASSUME	DS:DATA 						;AN000;
	CMP	[RELCNT],0		;Any fixups to do?
	JZ	STORE
	MOV	AX,[RELTAB]		;Get position of table

	push	dx
	push	cx
	push	ax
	push	bx
	mov	dx,ax
	xor	cx,cx
	mov	al,0
	mov	bx,[handle1]
	mov	ah,lseek
	int	21h
	jc	LseekError2
	pop	bx
	pop	ax
	pop	cx
	pop	dx

	MOV	DX,OFFSET RELPT 	;4-byte buffer for relocation address
RELOC:
	MOV	DX,OFFSET RELPT 	;4-byte buffer for relocation address
	MOV	CX,4
	MOV	AH,read
	push	bx
	mov	bx,[handle1]
	INT	21H			;Read in one relocation pointer
	pop	bx
	Jnc	RDCMP
	jmp	short DosError
RDCMP:
	MOV	DI,[RELPT]		;Get offset of relocation pointer
	MOV	AX,[RELSEG]		;Get segment
	ADD	AX,BX			;Bias segment with actual load segment
	MOV	ES,AX
	ASSUME	ES:NOTHING						;AN000;
	ADD	ES:[DI],BP		;Relocate
	DEC	[RELCNT]		;Count off
	JNZ	RELOC
STORE:
	MOV	AH,CREAT
	MOV	DX,OFFSET file2
	xor	cx,cx
	INT	21H
	Jc	MKERR
	mov	[handle2],ax
	MOV	CX,[SIZ]
	MOV	AH,write
	push	di
	mov	di,[handle2]
	PUSH	DS
	MOV	DS,BX
	ASSUME	DS:NOTHING						;AN000;
	XOR	DX,DX			;Address 0 in segment
	push	bx
	mov	bx,di
	INT	21H
	pop	bx
	POP	DS
	ASSUME	DS:DATA 						;AN000;
	pop	di
	Jc	WRTERR			;Must be zero if more to come
	cmp	AX,CX
	jnz	NOROOM
	MOV	AH,CLOSE
	push	bx
	mov	bx,[handle2]
	INT	21H
	jc	CloseError
	pop	bx
	pop	ds
	pop	ds
	ASSUME	DS:NOTHING						;AN000;

	RET

;*******************************************************************************

NOROOM: 				;				     ;AN000;
	ASSUME	DS:DATA 						;AN000;
	MOV	AH,CLOSE		; Close the file here		     ;AN000;
	push	bx			;				     ;AN000;
	mov	bx,[handle2]		;				     ;AN000;
	INT	21H			;				     ;AN000;
	jc	CloseError		; If error let extend messages get it;AN000;
	pop	bx			;				     ;AN000;
	mov	ah,UNLINK		; Delete the file because it did     ;AN000;
	MOV	DX,OFFSET file2 	; not get written correctly.	     ;AN000;
	INT	21H			;				     ;AN000;
	jc	CloseError		; If error let extend messages get it;AN000;
	pop	ds			;				     ;AN000;
	ASSUME	DS:NOTHING		;				     ;AN000;
	message msgNoDiskSpace		; Put out insufficient disk space    ;AN000;
	jmp	short getout		; message			     ;AN000;
	RET				; return to main_init		     ;AN000;

;*******************************************************************************

WRTERR: 								;AN000;
MKERR:									;AN000;
CloseError:								;AN000;

	public	DosError						;AN000;
DosError:								;AN000;
	mov	es:FileNameSegment,ds	   ; save for opens, creates,	;AN000;
	mov	es:FileNameOffset,dx					;AN000;

	mov	bx,0			; get the extended error code	;AN000;
	mov	ah,059h 						;AN000;
	int	21h							;AN000;

	mov	si,offset ds:Sublist_msg_exterror			;AC001;
	extend_message							;AN001;
	pop	ds							;AN001;

getout: 								;AN000;
	pop	ds							;AN000;
	ASSUME	DS:NOTHING						;AN000;

	ret								;AN000;


LOCATE	ENDP

;----- concatenate two strings
strcat	proc	near			;   while (*d)
	cmp	byte ptr [di],0
	jz	atend
	inc	di			;	d++;
	jmp	strcat
atend:					;   while (*d++ = *s++)
	lodsb
	stosb
	or	al,al			;	;
	jnz	atend
	ret
strcat	endp

;----- Find the first non-ignorable char, return carry if CR found
kill_bl proc	near
	cld
sj10:					;   while ( *p != 13 &&
	lodsb
	CMP	AL,13			;	    IsBlank (*p++))
	JZ	BreakOut
	CALL	IsBlank
	JZ	SJ10			;	;
BreakOut:
	dec	si			;   p--;
	cmp	al,0dh			;   return *p == 13;
	clc
	jne	sj11
	stc
sj11:
	ret
kill_bl endp

IsBlank proc	near
	cmp	al,00							;AN000;
	retz								;AN000;
	cmp	al,13
	retz
	cmp	al,' '			; space
	retz
	cmp	al,9			; tab
	retz
	cmp	al,','			; comma
	retz
	cmp	al,';'			; semicolon
	retz
	cmp	al,'+'			; plus
	retz
	cmp	al,10			; line feed
	retz
	cmp	al,'='			; equal sign
	return
IsBlank Endp


procedure copyfs near

	push	ax							;AN000;

;	$do				; while we have filespec	;AN000;
$$DO16:
	    lodsb			; move byte to al		;AN000;
	    cmp     al,0		; see if we are at		;AN000;
					; the end of the
					; filespec
;	$leave	e			; exit while loop		;AN000;
	JE $$EN16
	    stosb			; move byte to path_name	;AN000;
;	$enddo				; end do while			;AN000;
	JMP SHORT $$DO16
$$EN16:
	stosb								;AN000;
	pop	ax							;AN000;

	ret								;AN000;
copyfs	endp								;AN000;


procedure dbcs_check near

	push	ds				;Save registers 	;AC000;
	push	si				; "  "	  "  "		;AC000;
	push	ax				; "  "	  "  "		;AC000;
	push	ds				; "  "	  "  "		;AC000;
	pop	es				;Establish addressability;AC000;
	cmp	byte ptr es:DBCS_VECTOR,Yes	;Have we set this yet?	;AC000;
	push	ax				;Save input character	;AC000;
;	$IF	NE				;Nope			;AN000;
	JE $$IF19
	   mov	   al,0 			;Get DBCS environment vectors;AC000;
	   DOS_Call Hongeul			;  "  "    "  " 	;AC000;
	   mov	   byte ptr es:DBCS_VECTOR,YES	;Indicate we've got vector;AC000;
	   mov	   es:DBCS_Vector_Off,si	;Save the vector	;AC000;
	   mov	   ax,ds			;			;AC000;
	   mov	   es:DBCS_Vector_Seg,ax	;			;AC000;
;	$ENDIF					; for next time in	;AC000;
$$IF19:
	pop	ax				;Restore input character;AC000;
	mov	si,es:DBCS_Vector_Seg		;Get saved vector pointer;AC000;
	mov	ds,si				;			;AC000;
	mov	si,es:DBCS_Vector_Off		;			;AC000;
;	$SEARCH 				;Check all the vectors	;AC000;
$$DO21:
	   cmp	   word ptr ds:[si],End_Of_Vector ;End of vector table? ;AC000;
;	$LEAVE	E				;Yes, done		;AC000;
	JE $$EN21
	   cmp	   al,ds:[si]			;See if char is in vector;AC000;
;	$EXITIF AE,AND				;If >= to lower, and	;AC000;
	JNAE $$IF21
	   cmp	   al,ds:[si+1] 		; =< than higher range	;AC000;
;	$EXITIF BE				; then DBCS character	;AC000;
	JNBE $$IF21
	   stc					;Set CY to indicate DBCS;AC000;
;	$ORELSE 				;Not in range, check next;AC000;
	JMP SHORT $$SR21
$$IF21:
	   add	   si,DBCS_Vector_Size		;Get next DBCS vector	;AC000;
;	$ENDLOOP				;We didn't find DBCS chaR;AC000;
	JMP SHORT $$DO21
$$EN21:
	   clc					;Clear CY for exit	;AC000;
;	$ENDSRCH				;			;AC000;
$$SR21:
	pop	ax				;Restore registers	;AC000;
	pop	si				; "  "	  "  "		;AC000;
	pop	ds				;Restore data segment	;AC000;
	ret					;			;AC000;

	ret								;AN000;
dbcs_check  endp							;AN000;



CODE	ends


	end	main_init						;AC000;

