;    File              : $Workfile: CSTART.ASM$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log$
;    CSTART.ASM 1.2 97/03/21 15:01:01
;    Added /n option to disable critical error handler
;    CSTART.ASM 1.38 94/12/21 10:45:05 
;    Reduced Heap size to 860h just to be sure.
;    CSTART.ASM 1.34 94/03/29 16:10:05 
;    _docmd_int2f returns 1 or 0 depending on whether or not the command
;    is accepted.
;    CSTART.ASM 1.31 93/11/18 18:50:18
;    Fix HMA registration problem
;    CSTART.ASM 1.30 93/11/09 00:00:14
;    Shorten _ge_config_env
;    CSTART.ASM 1.28 93/11/05 00:54:25
;    move HMA registration code to where it's actually executed
;    CSTART.ASM 1.27 93/11/04 23:39:11
;    Fix problem with resident data relocation code
;    CSTART.ASM 1.26 93/11/04 20:06:09
;    int2E_far_entry now does CLI and STI the correct way round when loading
;    SS and SP.
;    CSTART.ASM 1.25 93/10/24 13:13:34
;    Added strategy 'best fit upper mem'' & link upper mem'
;    put_resident_high()    
;    CSTART.ASM 1.24 93/09/10 15:56:46
;    CLS checks for ANSI using Int 2F/1A00
;    CSTART.ASM 1.23 93/08/26 09:40:50
;    Now use PSP for stack during func 4b exec. There's some debug
;    code in case things screw up.
;    CSTART.ASM 1.21 93/08/03 10:04:18
;    Stopped using memory above A000 for transient portion because the 
;    stack was disappearing when MEMMAX -V executed.
;    CSTART.ASM 1.20 93/07/05 08:35:00
;    Now switch to a stack located at the top of conventional memory before
;    calling INT 21 ah=4B.
;    CSTART.ASM 1.19 93/05/24 11:38:22
;    alloc_com_memory now allocates copy buffer by allocating largest possible
;    block then shrinking it to the correct size. This prevents the buffer
;    being located in upper memory after a HILOAD.
;    CSTART.ASM 1.16 93/02/24 17:42:01
;    int10_cls() no longer checks position of INT 29 vector.
;    CSTART.ASM 1.12 92/11/17 14:26:40 
;    Change to set_reload_file to allow switches on COMSPEC line.
;    CSTART.ASM 1.10 92/09/17 11:31:43 
;    INT 2e DS=SI=0 now works when we are relocated.
;    CSTART.ASM 1.9 92/09/11 10:45:36
;    our_cmd_int2f altered so we can support multi-line macros in DOSKEY.
;    CSTART.ASM 1.8 92/08/06 09:55:22
;    Correctly support INT 2F AH=AEh for EDOS. See _cmd_line_int2f.
;    Int 2E DS=SI=0 causes batch processing to halt.
;    CSTART.ASM 1.7 92/07/20 17:13:44
;    Added the following maintanence source code changes:
;    29 Apr 92 Now sets Novell error mode to 00 on entry, and restores
;    ;		original mode on exit.
;    18 May 92 Added routine get_original_envsize, to use as default if
;    ;		/E option not used
;    ENDLOG

    page	62,132
	title	COMMAND.COM Startup Routines and Resident Section
;
;  7 Nov 87 Force the initial PATH specification to the root of the
;		boot drive. Also add a new variable TEMPDRV which is 
;		Concurrents Temporary Drive.
;
; 10 Dec 87 If DRNET has been loaded then add the DRNET=x.x string to
;		the initial environment.
; 25 Feb 88 Run the 2nd Phase RSP's before spawning the remaining TMPs
;  1 Mar 88 Initialize the AUTOLOGON and NODE variables for diskless
;		workstation support. Support the NODE environment variable
; 21 Mar 88 Allow for all registers to be corrupted on return from the 
;		DOS EXEC function call.
;  5 Apr 88 Correctly handle incorrect command line length passed by
;		FrameWork Install Program
; 13 Apr 88 Move Memory allocation code to DOSIF and initialise the
;		default console in PD for SunRiver.
; 10 May 88 Clean-Up the segment grouping and force the CGROUP to be
;		linked after the data for everything but the TMP. Add the
;		INT10_CLS function for DOSPLUS
; 12 May 88 Cater for DesqView passing an environment segment of 0000
; 13 May 88 Alter FCB build to giver F & RED when /FRED typed in For FS.COM
; 19 May 88 Prevent CODE being moved to high memory if loaded as an .EXE
;		or no initial environment is supplied
; 20 May 88 Move reload messages into the MESSAGE.C file.
; 24 May 88 Use the internal Critical Error handler and check for ABORT
;		codes while in the Command Processor Code.
;  6 Jun 88 Move Command ReadLine down to resident code - so SK+ can
;		overwrite the hi-mem portion.
; 16 Jun 88 VC_DATA now returns the Physical Console Number as well.
; 21 Jun 88 Add FARPTR routine which determines the correct segment to
;		return to PRINTF based on the offset of a message.
; 22 Jun 88  remove environment & PSP setup & RSP spawning code
;		for banked RSP support. Kludgey CDOS_EXEC code for banking
; 29 Jun 88 Modify MASTER_ENV so it can be called from C and change the
;		exec code to use the internal FCB parsing.
; 30 Jun 88 Set the default PRINTER and AUX using the information in the
;		INT17 and INT14 mapping arrays.
; 20 Jul 88 Increase HEAP Size to 0A00h for the TMP
;  9 Aug 88 Terminate the command line after a CR or LF for 
;		BitStream Fontware.
; 31 Aug 88 Make the READLINE function call from high memory so PolyTron
;		PolyWindows can be invoked from the command line.
; 22 Sep 88 Always use INT_BREAK routine for Control Break Handling
; 03 Oct 88 Invalidate the Old Environment for Novell under CDOS.
; 09 Nov 88 Select the correct DR-DOS history Buffer.
; 15 Nov 88 Re-initilaise Interrupt handlers after an EXEC for Novell
; 21 Nov 88 Install Command Processor Backdoor but just terminate caller
; 13 Dec 88 Generate the FCBs for a DOS exec internally for compatibility
;		with Novell Netware.
; 15 Dec 88 Force the default INT 22, 23, 24 and 2E handlers to be set 
;		relative to the PSP. Update PSP copies of interrupt 22, 23, 24
;		if this is the root DOS process.
; 25 Jan 89 If new DRDOS internal data layout get the PD a new way
; 27 Feb 89 fix MAKE_FCB, use SI from F_PARSE if possible
; 15 Apr 89 INT2E handler
; 17 Apr 89 int10_cls: don't return to 25 lines if in 43 or 50 line mode 
; 31 May 89 DRDOS get PD using new f4458 backdoor
;  6 Jun 89 int2e: amend our copy of command, not users command
; 19 Jun 89 Remove "Alternative" methods of getting PD address
; 14 Jul 89 SideKick Plus checksum only done if STDERR is to CON
;		(so when LAPLINK does CTTY COM1 it goes quicker)
;  6 Sep 89 Call INT21/5D09,5D08 in readline
; 30 Oct 89 Throw away startup code (put it in STACK segment)
; 13 Nov 89 Relocate DRDOS resident code over command line/fcbs in PSP
; 30 Jan 90 in_exec is now incremented and decremented to allow
;		novell menu program to exit successfully.
; 30 Jan 90 Added batch_seg_ptr before first occurance of string
;		'A:\COMMAND.COM' (reload_file). Novell uses it during
;		remote boot.
; 31 Jan 90 restore_term_addr puts back old Int22/23/24 ready for
;		an EXIT command (DESQview bug)
;		If no environment make reload_file in root of default drive
;  7 Feb 90 Turn off HILOAD on DRDOS
;		Add d2cgroupptr support routine (see COM.C)
; 27 Mar 90 turn history buffers to command when in readline, so COPY CON
;		etc will use application buffers
; 30 Mar 90  Stop cleanly when we can't reload transient portion rather
;		than overwriting other peoples memory and crashing
;  4 Apr 90 dbcs_init moved from DOSIF, use system table,
;		throw away init code
; 12 Apr 90 changed for no inherited environment
; 18 Apr 90 add JW's changes to int10_cls to support JW's new VGA card
;  9 May 90 Int2E doesn't trash Int24, returns with CY clear
; 17 May 90 CLI/STI round stack swap in INT2E exit routine.
;  5 Jun 90 Int21/4458 checks CY before fetching PD, so if running on DOS
;		we carry on into the C code which gives version error
; 12 Jun 90 master_env now leaves MS_M_STARTEGY alone, because Novell
;		gets confused if it ends up high
;  3/Jul/90 DLS data into R_TEXT for Watcom C v7.0 (again - I originally
;		did this on 20/Apr/90, but someone screwed up with the
;		archive version	managment).
;  2-Aug-90 RG-00- define LOGIN procedures for Stellar security
;  1-Sep-90 _msgfarptr added for DLS
; 13-Sep-90 COMSPEC=A:\COMMAND.COM even when CDOS.COM
; 21-Sep-90 Increase TMP heap size so we can save initial state for
;		subsequent login's.
; 26-Sep-90 We now switch to our own psp during an INT 2Eh.
; 		This fixes bug experienced with NOVELL MENU.EXE on top of
;		DR DOS 5.0.
; 11-Oct-90 CDOSTMP exec stashes unlinked MD in PSP for TSR auto-load
; 03-Dec-90 Stop corruption of DMD chain when allocating high memory for
;		transient portion of command processor 
; 11 dec 90 CDOS.COM switches to TMP history buffer
; 13 dec 90 save ES around TMP P_EXEC (DRNET sometimes trashes it)
; 17 dec 90 exec of CMD call io_pckbd with cl=40h (must be 24 line)
; 26/Feb/91 Increased _rld_msgs maximum text size from 100 to 120.
; 11/Mar/91 Added show_help function. COMMAND.COM is now really
;       an EXE file with the help messages tagged onto the end.
; 25/Apr/91 Most of the resident code/data is now relocatable to high
;		memory. Some Novell critical data is left in low memory.
; 15/May/91 Added dummy code to force at least one relocation item in
;		.exe header so loader doesn't think file is EXEPACKed.
; 12/Jun/91 Added dummy code to allow Software Carousel to run.
;		See int22_entry.
; 19 jun 91 disable control break until handler initialised
; 24/Jun/91 Changed memory allocation procedure on return from func 4B to
;		allow Novell Remote Boot to work.
;  2 jul 91 our_cmd_int2f only has single parameter
; 26 jul 91 Novell dummy pipe filenames zero terminated
; 29 jul 91 A 1K far buffer is now allocated by alloc_com_memory. It is
;		used by type, batch_read, and printf.
;  5 Aug 91 Call Get Extended Error (int 21 ah=59) after exec.
; 14 Aug 91 Put pointers to _batch, _batchflg, and _echoflg in low memory
;		stub. This is primarily for Software Carousel.
;  4 Dec 91 Fixed problem with full environment trashing next DMD.
; 29 Apr 92 Rearranged DGROUP so that constant code and data appear after
;		the stack - So that Multitasker need not save it.
; 10 Jun 92 show_help function now copes with doubled % characters.
;
;------------------------------------------------------------------------------

.xlist
include msdos.equ          
include mserror.equ
include char.def
.list


; This is the offset in segment FFFF to which we will relocate.
; It is set to E0 to allow for a PCNFS bug
HISEG_OFF	equ	0E0h


FALSE		equ	0h
TRUE		equ	not FALSE

ThreeCOM	equ	TRUE


Copy_Buffer_Size equ	0C80h		; 50k of buffer space in paras
					; matches MAX_COPYBUF in COMCPY.C
RLSTACK_SIZE	equ	256+256		; Reserve for ReadLine stack
					; We need 260 bytes for possible
					; buffer, plus a little stack
					; If the stack overflows it isn't
					; a disaster - we will just re-load
           				; COMMAND.COM

ifdef CDOSTMP
C_HEAP_SIZE	equ	1200h		; C routine Heap Size - TMP has
else					;  extra to save ENV in
;C_HEAP_SIZE	equ	0800h		; C routine Heap Size
;endif					; (observed sizes 500h-600h - IJ)
C_HEAP_SIZE	equ	0860h		; C routine Heap Size
; For safety increased that value as UNC filenames require 128 byte buffers
; allocated dynamically on tha stack. With respect to the observed sizes
; above it might be dangerous to leave that value at 0800h. I would have
; increased the value to 0A00 but then it does no longer fit into HMA. (JBM)
endif

ifdef DOSPLUS
include f52data.def
else
.xlist
include	system.def
include pd.def
.list

XIOS_HISTBUF	equ	44		; Get the History Buffer Address
XIOS_PCKBD	equ	32		; Set keyboard/screen modes

NETMOD_BIT	equ	040h		; Network Module in MODULE_MAP

; Concurrent DOS System Data Page Format
XIOS_ENTRY	equ	ds:dword ptr 0028h	; XIOS function Entry
CCPMSEG		equ	es:word ptr 0040h	; OS Starting Paragraph
MODULE_MAP	equ	es:byte ptr 0046h	; Concurrent Module Map
NVCNS		equ	es:byte ptr 0047h	; Number of Virtual Consoles
MFL		equ	es:word ptr 005Ah	; Memory Free List
RLR		equ	es:word ptr 0068h	; Ready List Root	
VERSION		equ	es:word ptr 0078h	; OS Version String Offset
MWDR		equ	es:word ptr 0098h	; Memory Window Decriptor
BOOTDRV		equ	es:byte ptr 009Dh	; System Boot Drive
ENVSIZE 	equ	es:word ptr 00B8h	; Environment Size Bytes
DRNET_NODE	equ	es:byte ptr 00BAh	; DR-NET Node Number
DRNET_FLAG	equ	es:byte ptr 00BB	; DR-NET Flags
V386_PTR	equ	es:word ptr 00C8h	; 386 Data pointer

CCBLIST		equ	es:word ptr 0C3Eh	; XIOS CCB$LIST
INT17_PTR	equ	es:word ptr 0C46h	; XIOS INT 17 Mapping Array
INT14_PTR	equ	es:word ptr 0C48h	; XIOS INT 14 Mapping Array

CCB_OWNER	equ	es:word ptr 0		; CCB Owning Process
CCB_PCNS	equ	es:byte ptr 10		; CCB Physical Console Number
CCB_VCNS	equ	es:byte ptr 11		; CCB Virtual Console Number

MF_CODE		equ	0004h			; MD flag bit
endif
;
;	Standard definitions for PSP variable
;
PSP_TERM_IP	equ	es:word ptr 000Ah
PSP_PARENT	equ	es:word ptr 0016h
PSP_ENVIRON	equ	es:word ptr 002ch


ifndef DOSPLUS
;	The following Macro takes one parameter which is the Concurrent DOS
;	function number.

bdos	MACRO	func
ifnb	<func>
	mov	cl,func
endif
	int	224
	ENDM

.xlist
include	ccpm.equ
.list
endif

swap	MACRO	reg1, reg2
	push	reg1
	push	reg2
	pop	reg1
	pop	reg2
	ENDM
	
	page
ifdef CDOSTMP
;
;	For the Concurrent DOS TMP the CGROUP segments are defined
;	first so that the CGROUP appears first in the .EXE file and
;	can be converted to a .COM by "RELOC" or "EXE2BIN". This .COM
;	file is then converted to a .CMD by the COM2CMD utility which
;	uses the data embeded at the start of the code to generate the
;	right groups.
;
HGROUP	GROUP	HEADER
HEADER		SEGMENT para public 'HEADER'
HEADER		ENDS

DGROUP	GROUP	RSP_SEG,PD_SEG,UDA_SEG,NULL,EXEC_CODE,_DATA,DATA,CONST,FIXED,_BSS,HEAP,c_common,STACK,DYNAMIC

RSP_SEG		SEGMENT para public 'CDOS_DATA'
RSP_SEG		ENDS
PD_SEG		SEGMENT para public 'CDOS_DATA'
PD_SEG		ENDS
UDA_SEG		SEGMENT para public 'CDOS_DATA'
UDA_SEG		ENDS
NULL		SEGMENT para public 'BEGDATA'
NULL		ENDS
EXEC_CODE	SEGMENT byte public 'DATA'
EXEC_CODE	ENDS
_DATA		SEGMENT byte public 'DATA'
_DATA		ENDS
DATA		SEGMENT byte public 'DATA'
DATA		ENDS
CONST		SEGMENT byte public 'CONST'
CONST		ENDS
FIXED		SEGMENT para public 'FDATA'
FIXED		ENDS
_BSS		SEGMENT word public 'BSS'
_BSS		ENDS
HEAP		SEGMENT word public 'BSS'
HEAP		ENDS
c_common	SEGMENT byte public 'BSS'
c_common	ENDS
STACK		SEGMENT para public 'BSS'
STACK		ENDS
DYNAMIC		SEGMENT para public 'DDATA'
DYNAMIC		ENDS
endif

ifndef CDOSTMP
;	The following declarations declare the presence and order of
;	various data segments within the DATA Group of the command
;	processor.
;
DGROUP	GROUP	R_TEXT, ED_TEXT, NULL, _DATA, DATA, CONST, FIXED, _BSS, HEAP, c_common, STACK, DYNAMIC

R_TEXT		SEGMENT para public 'CDOS_DATA'
R_TEXT		ENDS
NULL		SEGMENT byte public 'BEGDATA'
NULL		ENDS
_DATA		SEGMENT byte public 'DATA'
_DATA		ENDS
DATA		SEGMENT byte public 'DATA'
DATA		ENDS
CONST		SEGMENT byte public 'CONST'
CONST		ENDS
FIXED		SEGMENT byte public 'FDATA'
FIXED		ENDS
_BSS		SEGMENT byte public 'BSS'
_BSS		ENDS
HEAP		SEGMENT byte public 'BSS'
HEAP		ENDS
c_common	SEGMENT byte public 'BSS'
c_common	ENDS
STACK		SEGMENT word public 'STACK'
STACK		ENDS
ED_TEXT		SEGMENT para public 'CDATA'
	Public	ed_text_start
ed_text_start	label byte
ED_TEXT		ENDS
DYNAMIC		SEGMENT para public 'DDATA'
DYNAMIC		ENDS
endif

CGROUP	GROUP	_TEXT, _MSG, _TEXTEND
_TEXT		SEGMENT para public 'CODE'
ifdef	DLS
	extrn	_my_dls_init:far
endif
_TEXT		ENDS

_MSG		SEGMENT byte public 'CODE'
_MSG		ENDS

_TEXTEND	SEGMENT para public 'CODE'
_TEXTEND	ENDS


CEND	GROUP	ETEXT, ETEXTEND
ETEXT		SEGMENT para public 'XEND'
ETEXT		ENDS
ETEXTEND	SEGMENT para public 'XEND'
ETEXTEND	ENDS


codeOFFSET	equ	offset CGROUP:
dataOFFSET	equ	offset DGROUP:
endOFFSET	equ	offset CEND:

code_length		equ	codeOFFSET rlstack 	; Total Code Length
real_code		equ	code_length - RLSTACK_SIZE
static_length		equ	dataOFFSET FIXED	 ; Static Data Length
dynamic_length		equ	dataOFFSET ed_text_start
total_length		equ	dataOFFSET DYNAMIC	 ; Total Data Length
cgroup_length		equ	codeOFFSET _TEXTEND
cend_length		equ	endOFFSET ETEXTEND

; help_length is an APPROXIMATE value, but it must be LARGER than the correct
; length of the help segment.
ifdef DLS
help_length		equ	0A000h		 
else
help_length		equ	05000h
endif

page		
public	__acrtused		; trick to force in startup
	__acrtused = 9876h	; funny value not easily matched in SYMDEB

ifdef CDOSTMP
HEADER		SEGMENT public 'HEADER'
	dw	0EDCh				; Header Signature
	dw	offset HGROUP:CGROUP		; Code Group
	dw	real_code			; Real Code Size
	dw	offset HGROUP:DGROUP
	dw	total_length
	dw	static_length
HEADER		ENDS

;	These Segments are forced into the correct order for a CDOS 
;	Resident System Process. First the RSP header which contains
;	the regeneration information required by GENSYS.
;
RSP_SEG	SEGMENT
sysdatseg	dw	0			; system data segment
sdatvar		dw	0047h			; # of system consoles
defconsole	db	0,0			; console # | copy #
		dw	0,0
		dw	RSF_DYNAMIC+RSF_SPECIAL+RSF_ENVIRON
		dw	0
		dw	0

RSP_SEG	ENDS

_DATA		SEGMENT byte public 'DATA'
	extrn	_gp_far_buff:word
_DATA		ENDS

else
R_TEXT	SEGMENT
	assume cs:DGROUP, ds:nothing, es:nothing, ss:nothing

ifdef	WATCOMC
	Public	_small_code_		; Watcom C requires this label to
_small_code_	label	near		; be declared in the start-up module
else
ifdef	MWC
	Public	_mwINIT			; MetaWare requires this label
_mwINIT		label	near		; to be declared in the start-up
					; module
else
    Public  __cstart       
__cstart	label	near		; to be declared in the start-up
					; module
endif
endif

	extrn	_gp_far_buff:word

ifdef	DOSPLUS
	extrn	com_criterr:near
endif


cstart:					; start address of all "C" programs
	call	near ptr getIP		; Push the IP register and Skip the
retIP:					; version control messages.
	dw	0EDCh			; Digital Research Marker
	dw	code_length		; Length of the Code Group
	;dw	static_length		; Length of the Fixed Data Group
ifdef DLS
	dw	total_length
else
	dw	dynamic_length		; length of dynamic data
endif
	dw	total_length		; Minimum Length of the Runtime
					; Data Group

reloc_off	dw 0		; offset of relocated resident code/data 
reloc_seg	dw 0		; segment of relocated resident code/data
reloc_size	dw 0		; size of relocated resident code/data

	public	_batchptr_off
	public	_batchflg_off
	public	_echoflg_off
_batchptr_off	dw 0		; offset of _batch variable
_batchflg_off	dw 0		; offset of _batchflg variable
_echoflg_off	dw 0		; offset of _echoflg variable

; These are the entry points for INT 23 and INT 24. They will have JMPF
; instructions poked into them.
control_break_entry	db 5 dup(90h)
crit_error_entry	db 5 dup(90h)


; The call to MS_X_EXEC must be made from the same segment as the PSP.
; msdos_exec does a far jump to here and then we far jump back.

psp_dofunc4b:
		mov	ax,(MS_X_EXEC*256)
		int	DOS_INT
		jmp  	i22_entry

; Software Carousel Version 5.0 looks for the following three instructions
; and assumes it is an entry point to int 22 code.
		mov	bx,0ffffh
		mov	ah,48h
		int	21h
		clc
i22_entry:		
int22_entry	db	0eah
		dw	dataOFFSET func4b_return
func4b_seg	dw	0

int2E_entry	db	0eah
		dw	dataOFFSET int2E_far_entry
int2E_seg	dw	0


; IMPORTANT - batch_seg_ptr MUST be just before reload_file. - EJH
;
;	***** Do Not change the order of the following variables *****
;

batch_seg_ptr	dw 0ffffh		; file filename. For novell remote
					; boot support.

ifdef DOSPLUS
reload_file	db	'A:\COMMAND.COM',0
else
ifdef NETWARE
reload_file	db	'A:\NETWARE.COM',0
else
reload_file	db	'A:\CDOS.COM',0
endif
endif
		db	(80-15) dup ('n')	; Expanded LoadPath

;cmdline	db	128 dup ('c')	; Local Copy of Initial Command Line

;	dummy pipe filenames for NOVELL

out_pipe 	db	'_:/' , 0 , '_______.___',0
in_pipe		db	'_:/' , 0 , '_______.___',0

;
;	***** Do Not change the order of the preceeding variables *****
;

; This next bit forces hi_seg_start to be on a paragraph boundary 
	org	HISEG_OFF
hi_seg_start	label byte

; Himem Registration chain entry
himem_link_next	dw	0
himem_link_size	dw	0
		db	5

	public	__psp
__psp		dw	0

	Public	_batch_seg_ptr
_batch_seg_ptr	dw	dataOFFSET batch_seg_ptr
		dw	0	; segment will be set to low_seg

	Public	_cbreak_ok

_cbreak_ok	db	0	; set when ctrl-break handler initialised

	; The following causes there to be at least one relocation item
	; in the .exe header so the loader does not think the file is
	; EXEPACKed.
	mov	ax,seg _batch_seg_ptr

R_TEXT	ENDS

endif

_TEXT	SEGMENT
ifdef  CDOSTMP
	assume cs:CGROUP, ds:DGROUP, ss:DGROUP
;
;	This entry point is used when the startup is executed as an
;	RSP. CS is CGROUP and DS is DGROUP. From here on we initialise
;	internal data structures etc. 
;

ifdef	WATCOMC
	Public	_small_code_		; Watcom C requires this label to
_small_code_	label	near		; be declared in the start-up module
else
ifdef	MWC
	Public	_mwINIT			; MetaWare requires this label
_mwINIT		label	near		; to be declared in the start-up
					; module
else
    Public  __cstart       
__cstart	label	near		; to be declared in the start-up
					; module
endif
endif

cstart:
	call	RSP_start		; Push the IP register and Skip the
retIP:					; version control messages.
	dw	0EDCh			; Digital Research Marker
	dw	code_length		; Length of the Code Group
	dw	static_length		; Length of the Fixed Data Group
	dw	total_length		; Minimum Length of the Runtime
					; Data Group
else
 	extrn	_int2e_handler:far
endif
	extrn	__main:far		; C main program


_TEXT	ENDS

_DATA	SEGMENT
;
;	Data held in this segment remains in the resident portion of 
;	the program image is is not overlayed by transient programs
;	loaded by COMMAND.COM. The variables here are private to the
;	startup module.
;
psp_save_area	dw	6 dup (?)

ifndef DOSPLUS
	extrn	_pd:dword		; Concurrent Process Descriptor
	extrn	_sysdat_seg:word	; Concurrent System Data Page
endif

	extrn	_n_option:word
;
;	The following is the offset and segment of the C routine MAIN
;	which is moved up in memory in order to accomodate the
;	Environment variables and Resident Data area.
;
C_code_entry	label	dword
	Public	code_seg,low_seg	
code_off	dw	codeOFFSET __main	; Offset of MAIN
code_seg	dw	?			; Segment of MAIN
data_seg	dw	?			; DGROUP segment
alloc_seg	dw	?			; the start of hi mem allocated
low_seg		dw	?			; segment of low memory stub.

ifdef CDOSTMP	

	Public	__psp
__psp		dw	0

cmd_histbuf	dw	0		; Command Processor History Buffer
prog_histbuf	dw	0		; Program History Buffer

mpb		label	word
mpb_start	dw	?
mpb_min		dw	?
mpb_max		dw	?
mpb_pdadr	dw	?
mpb_flags	dw	?
	
sysdat		dw	?		; Concurrent System Data Page
uda		dw	?		; Concurrent User Data Area
;
;	The following buffer is used by the P_EXEC function. Used
;	by CDOS_EXEC to load DOS and CP/M programs.
;
exec_block	label	byte
exec_pathoff	dw	?		; Offset of ASCIIZ Load file
exec_pathseg	dw	?		; Segment of ASCIIZ Load File
exec_filetype	db	?		; File Type Index
exec_loadtype	db	?		; EXEC or CHAIN to application
exec_clineoff	dw	?		; ASCIIZ Command line Offset
exec_clineseg	dw	?		; ASCIIZ Command Line Segment

exec		label	dword		; FAR pointer to EXEC routine
		dw	dataOFFSET cdos_exec
exec_seg	dw	?

cmdline		db	0		; Blank Command Line

EXEC_CODE	SEGMENT

	Assume	CS:DGROUP, DS:DGROUP, SS:DGROUP

err_tbl		db	0		; 00 Success
		db	-101		; 01 System Call Not Implemented
		db	-102		; 02 Illegal System Call
		db	ED_MEMORY	; 03 Cannot Find Memory
		db	-104		; 04 Illegal Flag Number
		db	-105		; 05 Flag Overrun
		db	-106		; 06 Flag Underrun
		db	-107		; 07 No Unused Queue Descriptors
		db	-108		; 08 No free Queue Buffer
		db	-109		; 09 Cannot find Queue
		db	-110		; 10 Queue in Use
		db	-111		; 11
		db	-112		; 12 No Free Process Descriptors
		db	-113		; 13 No Queue Access
		db	-114		; 14 Empty Queue
		db	-115		; 15 Full Queue
		db	-116		; 16 CLI Queue missing
		db	-117		; 17 No 8087 in system
		db	ED_DMD		; 18 No Unused Memory Descriptors
		db	-119		; 19 Illegal Console Number
		db	-120		; 20 No Process Descriptor Match
		db	-121		; 21 No Console Match
		db	-122		; 22 No CLI Process ??
		db	-123		; 23 Illegal Disk Number
		db	-124		; 24 Illegal FileName
		db	-125		; 25 Illegal FileType 
		db	-126		; 26 Character Not Ready
		db	ED_BLOCK	; 27 Illegal Memory Descriptor
		db	-128		; 28 Bad Return from BDOS load
		db	ED_FAIL		; 29 Bad Return from BDOS read
		db	ED_ACCESS	; 30 Bad Return from BDOS Open
		db	-131		; 31 Null Command
		db	ED_ENVIRON	; 32 Not owner of resource
		db	-133		; 33 No Cseg in Load File
		db	-134		; 34 PD exists on Thread Root
		db	-135		; 35 Could Not Terminate Process
		db	-136		; 36 Cannot ATTACH to Process
		db	-137		; 37 Illegal List Device Number
		db	ED_PASSWORD	; 38 Illegal Password
		db	-139		; 39
		db	-140		; 40 External Termination
		db	-141		; 41 Fixup Error on Load
		db	-142		; 42 Flag Set Ignored
		db	-143		; 43 Illegal Aux Device Number

cdos2dos PROC NEAR
	cmp	ax,0000			; Check for Success
	jz	c2d10			; and skip lookup
	lea	bx,err_tbl		; xlat the error code in AL
	xlat	err_tbl			; into a Negated DOS compatible
	mov	ah,0FFH			; error code
c2d10:
	ret
cdos2dos ENDP

; WORD FAR CDECL cdos_exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back);
;
; On Entry:
;	back	10[bp]
;	line	08[bp]
;	type	06[bp]
;	path	04[bp]
;
;	ES = SYSDAT
;
; On Exit:
;	AX = exit error code
;
cdos_exec PROC FAR
	lea	si,P_MEM[bx]		; SI -> root of MD's
ce_10:					; find our code segment
	mov	si,es:[si]		; get next memory descriptor
	test	si,si			; end of list?
	 jz	ce_40			; yes, we don't own any separate code
	test	es:word ptr 6[si],MF_CODE
	 jz	ce_10			; loop back if not code segment
	mov	si,es:8[si]		; get MPAD for code segment
	lea	di,P_MPAR[bx]		; get MPAD root
ce_20:
	cmp	si,es:[di]		; is this the predecessor?
	 je	ce_30
	mov	di,es:[di]		; else check next MPAD
	jmp	short ce_20
ce_30:					; SI -> our MPAD, DI -> previous MPAD
	xor	ax,ax
	xchg	ax,es:[si]		; get next MPAD
	mov	es:[di],ax		; unlink our MPAD from list
ce_40:
	push	di
	push	si			; SI = 0 if no separate code alloc

	push	ds
	mov	ds,es:P_PSP[bx]		; point to our PSP
	mov	ds:word ptr [5eh],si	; stash TMP mpad away in here
	pop	ds

	mov	dx,198			; Raise the priority of the TMP
	bdos	P_PRIORITY		; while we wait for the child

	mov	dx,dataOFFSET exec_block
	push	es			; do a P_EXEC but save /restore
	bdos	P_EXEC			; ES around it since DRNET trashes
	pop	es			; it sometimes.

	mov	ax,cx			; Get the Concurrent Error Code
	call	cdos2dos		; and convert to a standard DOS
	push	ax
					; Error Code for COMMAND.RSP
	test	ax,ax			; If any errors occured during the 
	 jnz	ce_60			; exec or the child process is not
	cmp	word ptr 10[bp],0	; inheriting the console the do not
	 jnz	ce_60			; execute a Console Attach function
	bdos	C_ATTACH		; process to terminate
ce_60:	
	mov	dx,200			; Return to the normal priority
	bdos	P_PRIORITY		; now that we have the console back
	pop	ax
	pop	si
	pop	di
	test	si,si			; separate code allocation?
	 jz	ce_50			; no, don't have to un-kludge!
	push	ax
	mov	ax,es:[di]		; get link of previous MPAD
	mov	es:[si],ax		; link it to our MPAD
	mov	es:[di],si		; link our MPAD to previous MPAD
	mov	dx,1			; make sure code gets banked in
	bdos	P_DELAY			; this gets us off/on RLR
	pop	ax
ce_50:
	ret
cdos_exec ENDP	

EXEC_CODE	ENDS

_TEXT	SEGMENT
	assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing
	public	_exec			; EXEC routine

;
; WORD CDECL exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back);
;
; On Entry:
;	back	10[bp]		; This value is ignored for MSDOS_EXEC
;	line	08[bp]
;	type	06[bp]
;	path	04[bp]
;
; On Exit:
;	AX = exit error code
;
_exec:
;-----
	push	bp
	mov	bp,sp
	push	si
	push	di
	mov	ax,04[bp]		; Get the Full Command Name
	mov	exec_pathoff,ax
	mov	exec_pathseg,ds

	mov	ax,08[bp]		; and the Command Line
	mov	exec_clineoff,ax
	mov	exec_clineseg,ds

	mov	ax,06[bp]		; Get the Command Type (.CMD etc)
	mov	exec_filetype,al	; Save the Command type
	mov	exec_loadtype, 0	; The default is to load the command
	cmp	word ptr 10[bp],0	; and for the child to inherit the
	 jz	exec_10			; console
	mov	exec_loadtype,2
exec_10:

	test	al,al			; is command type a CMD
	 jnz	exec20			; no, skip XIOS call
	mov	ax,XIOS_PCKBD		; CMD's expect 24 lines
	mov	cl,40h			; so tell XIOS thats what we must have
	mov	dl,defconsole		; on this console
	call	xios
exec20:

	les	bx,_pd			; Get the process Descriptor Address
	mov	cx,prog_histbuf		; and force the system to use the
	mov	es:P_SB_SEG[bx],cx	; Program Level History Buffer

	call	exec			; do FAR call to cdos_exec

	les	bx,_pd			; Get the process Descriptor Address
	mov	cx,cmd_histbuf		; and force the system to use the
	mov	es:P_SB_SEG[bx],cx	; Command Level History Buffer

	cmp	exec_filetype,0		; was it a CMD
	 jne	exec30
	push	ax
	mov	ax,XIOS_PCKBD		; we are in must-be-24 line
	mov	cl,0			; mode, get back to default mode
	mov	dl,defconsole		; for console number
	call	xios
	pop	ax
exec30:
	pop	di
	pop	si
	pop	bp
	ret

_TEXT	ENDS


	Assume	CS:DGROUP, DS:DGROUP, SS:DGROUP
else
;
;	Novell 2.1 intercepts the DOSPLUS 4B00  return by updating the
;	PSP USER_SS/SP and  when it returns ALL registers except CS:IP
;	have been corrupted.
;
	extrn	stack_min:word		; Minimum Stack Address
	extrn	heap_top:word

readline	label	dword		; FAR pointer to READLINE routine
		dw	dataOFFSET msdos_readline
readline_seg	dw	?

critical	dd	?		; Critical Error Handler Address
exec_sp		dw	?
exec_ss		dw	?

exec_block	label	byte
exec_env	dw	?		; Environment Segment
exec_clineoff	dw	?		; ASCIIZ Command line Offset
exec_clineseg	dw	?		; ASCIIZ Command Line Segment
exec_fcb1off	dw	dataOFFSET fcb1	; FCB1 Contents Offset
exec_fcb1seg	dw	?		; FCB1 Contents Segment
exec_fcb2off	dw	dataOFFSET fcb2	; FCB2 Contents Offset
exec_fcb2seg	dw	?		; FCB2 Contents Segment

exec		label	dword		; FAR pointer to EXEC routine
		dw	dataOFFSET msdos_exec
exec_seg	dw	?

msdos_exec_ret	label	dword		; FAR pointer to exit EXEC routine
		dw	codeOFFSET _exec_ret
msdos_exec_ret_seg dw	?

fcb1		db	16 dup (?)	; FCB1 Buffer
fcb2		db	16 dup (?)	; FCB2 Buffer
crc		dw	?		; COMMAND.COM crc

; For Dual Language Support...
ifdef DLS
;;ED_TEXT		SEGMENT para public 'CDATA'

	Public	_rld_msgs,_rld_text
_rld_msgs	dw	120 ;RELOAD_LEN	; size of this message buffer
_reload_msgs	dw	0		; First part of Code reload prompt
_reload_msgf	dw	0		; Second part of Code reload prompt
_reload_msgm	dw	0		; Unlikely part of Code reload prompt
		dw	0		; end of list
_rld_text	db	120 dup (?)	; message text is placed here
RELOAD_LEN	equ	$-_reload_msgs
;;ED_TEXT		ENDS
else
	extrn	_reload_msgs:byte	; First part of Code reload prompt
	extrn	_reload_msgf:byte	; Second part of Code reload prompt
	extrn	_reload_msgm:byte	; No memory available message
endif


reload_flag	db	0		; Reloading Command Processor	
in_exec     db  0       
high_code	dw	TRUE		; Enable High Code
exe_file	dw	FALSE		; True if COMMAND.COM is really an EXE
return_code	dw	?		; Exec return code
net_error_mode  db	?

; int2E data

i2e_lock	dw	0		; mutex flag
i2e_user_ss	dw	0		; users ss, sp
i2e_user_sp	dw	0
i2e_stack	dw	0
i2e_cmd		dw	0		; offset of local copy of command line

i2e_c_entry	label	dword
i2e_c_offs	dw	codeOFFSET _int2e_handler
i2e_c_seg	dw	?

i2e_i23vec	label	dword
i2e_i23off	dw	?
i2e_i23seg	dw	?

i2e_i24vec	label	dword
i2e_i24off	dw	?
i2e_i24seg	dw	?

endif
_DATA	ENDS

_BSS	SEGMENT
Public	_edata
_edata	label	BYTE		; end of data (start of bss)
_BSS	ENDS

ETEXT	SEGMENT
;
;	The RLSTACK segment holds the stack used by the READLINE routine.
;	This must be in High memory for the PolyTron PolyWindows product.
;
;	"ETEXT" also forces the linker to pad the CGROUP to at least
;	real_code bytes. Otherwise the file length can be upto 15 bytes 
;	shorter then real_code + total_length. This obviously causes 
;	problems with CALC_CRC and the file reloading.
;
		db	RLSTACK_SIZE dup(0CCh)
rlstack		label	word

ETEXT	ENDS

ifdef CDOSTMP
STACK	SEGMENT

Public	_end
_end	label	BYTE		; end of bss (start of starup/stack)

 		db	(C_HEAP_SIZE - 6) dup (0DDh)	; C Heap Area
stack_top	label	word
stack_ip	dw	?		; Initial Offset	
stack_cs	dw	?		; Initial Code Segment (Unknown)
stack_flags	dw	?		; Initial Flags (Unknown)

temp_buffer	db	512 dup(0)	; temp far buffer for *gp_far_buff

STACK	ENDS
else

STACK	SEGMENT

stack_start:
;	HEAP_TOP is initialised to _end so the _RELOAD_FILE and CMDLINE
;	variables are allocated on the HEAP.
;

Public	_end
_end	label	BYTE		; end of bss (start of startup/stack)


	db	255,'3771146-XXXX-654321'

	page
;
;	The CS register is now adjusted so that this startup
;	can be used in the form of an .EXE or .COM. Whatever the 
;	execution format of this file DS still points at the PSP
;	but must be careful when resizing the RAM.
;
getIP	PROC	FAR
	cld				; be sure of DIR flag...
	pop	di			; Get the Program Counter
	sub	di,dataOFFSET retIP	; Correct for retIP
	mov	cl,4			; Convert Offset values to Segments
	mov	ax,cs			; Get the current CS
	shr	di,cl			; Convert Initial IP to Segment
	add	ax,di			; add to CS and save for CALLF to MAIN
	push	ax			; Save the New CS and the offset to
	mov	ax,dataOFFSET gotCS	; the next instruction and then execute
	push	ax
	ret				; a RETF instruction to correct CS.
getIP	ENDP

ifdef DOSPLUS

; Most of the PSP (FCB's and command buffer) is unused - we reclaim this space
; by relocating the resident part of COMMAND.COM

reloc_code:
;----------
; On Entry:
;	CS = DGROUP, DS = PSP, ES = nothing
; On Exit:
;	CS = relocated DGROUP
;
; We build a "REP MOVSB ! RETF 6" on the stack. We also fiddle the near return
; address into a FAR. We then setup our registers appropriately and execute
; this code.
;
if 1
	ret
else
	cmp	ds:byte ptr 0080h,7fh	; discard garbage cmdline lengths
	jb	reloc_code10
	ret

reloc_code10:	 
	pop	bx			; recover return offset

	mov	ax,0
	push	ax			; 0 (part of RETF 6)
	mov	ax,006cah
	push	ax			; RETF 6 on stack

	mov	ax,0a4f3h
	push	ax			; REP MOVSB on stack

	mov	al,ds:0080h		; Get the Command length
	xor	ah,ah
	mov	cl,4			; convert AX to cmdline length
	shr	ax,cl			; in rounded down para's
	add	ax,9			; keep at least this much
	mov	si,ds
	add	ax,si			; add in PSP
	mov	es,ax
	mov	cx,sp			; save address of code on stack
	push	ax
	push	bx			; new RETF address on stack

	push	ss
	push	cx			; address of CODE on stack

	push	cs
	pop	ds			; DS -> DGROUP
	xor	si,si			; setup DS:SI, ES:DI and CX
	xor	di,di			;  ready to do REP MOVSB
	mov	cx,total_length		; DGROUP length
	add	cx,real_code		;+CGROUP length
	
	db	0cbh			; RETF to code on stack
endif	
endif

gotCS:
	;out	0fdh,al			; for debug purposes
	
	mov	[__psp],ds		; Save our PSP in the local variable
ifdef DOSPLUS
	mov	ax,cs			; put our stack somewhere safe
	mov	ss,ax
	mov	sp,dataOFFSET rlstack
	push	ds
	push	di
	call	reloc_code		; relocate code over unused bit of PSP
	pop	di
	pop	ds
endif
ifdef DLS
	call	_my_dls_init
endif

	mov	[code_seg],cs		; Initialise the DATA segment address
	mov	[data_seg],cs		; and calculate the current address
	mov	[exec_seg],cs		; of the Code Segment
	mov	[readline_seg],cs	; use it to fixup some JMPFs
	mov	[_batch_seg_ptr+2],cs
	mov	[low_seg],cs	    ; we may be relocated to upper or high
				    ; memory so remember the current segment

	mov	bx,cs			; Path up the JMPF instructions
	sub	bx,[__psp]		; around the MSDOS EXEC code to
	mov	cl,4
	shl	bx,cl
	add	cs:[exec_psp-2],bx
	mov	cs:[func4b_seg],cs
	mov	cs:[int2E_seg],cs
	mov	cs:[exec_psp],ds

	cmp	di,0000h		; Disable Code Relocation if we have
	jnz	gotCS_10		; been loaded as an .EXE file
	;;mov	high_code,FALSE
	
	mov	exe_file,TRUE		; Remember we are an EXE

gotCS_10:
	mov	di,total_length
	shr	di,cl
	add	[code_seg],di

	push	ds			; Initialise the Checksum so we can
	mov	ds,[code_seg]		; check the integrity of the high
	mov	si,2			 ; high copy of the command processor
	call	calc_crc		; code
	pop	ds
	mov	[crc],ax

	mov	ah,0ddh			; set Novell error mode
	mov	dl,0			; to 00 - BAP
	int	21h
	mov	net_error_mode,al	; save original error mode

	call	get_ds			; Get DGROUP value in AX
	cli				; turn off interrupts
	mov	ss,ax			; SS = DGROUP
	mov	sp,dynamic_length	; Initialise SP
	;mov	sp,total_length
	sti				; turn interrupts back on

	assume	ss:DGROUP

	push	ax
	call	handler_init		; Initialise the Control Break and
	pop	ax			; Critical Error Interrupt Vectors

	mov	si,total_length		; Get the DGROUP length in bytes
	add	si,code_length		; Add in the Code Length
	mov	cl,4			; and convert to a paragraphs
	shr	si,cl
	mov	bx,ax			; Get the Current DS
	sub	bx,__psp		; and calculate DS - PSP Seg
	add	bx,si			; DS + Data length in Para's
	mov	es,__psp
	mov	ah,MS_M_SETBLOCK
	int	DOS_INT

	call	exec_name		; Get our Load Path from the environment

	cmp	high_code,FALSE		; Skip Memory check if HIGH_CODE
	jz	cstart_10		; support has been disabled

	mov	high_code,FALSE		; Assume Failure.
	
	call	alloc_com_memory	; Allocate high memory for command.com
	jnc	carry_on
	
	mov	ax,cs			; if no memory and CS is nearing
	cmp	ax,8000h		; transient part abort and go home.
	jb	cstart_10
	
	mov	ah,4ch
	int	21h	

carry_on:
	mov	high_code,TRUE		; Set HIGH_CODE flag TRUE	
	push	es			; Relocate the command processor code
	push	ds			; into high memory
	mov	es,ax			; es-> destination segment
	mov	di,0
	mov	si,0
	mov	ds,code_seg		; ds-> code to be moved
	mov	cx,real_code		; convert bytes to words
	shr	cx,1
	rep	movsw			; Move code up to high memory
	mov	code_seg,es		; update code_seg

	pop	ds
	pop	es
		
; Shrink memory containing low memory version of step aside code
	mov	si,total_length		; Get the DGROUP length in bytes
	mov	cl,4			; and convert to a paragraphs
	shr	si,cl
	call	get_ds
	mov	bx,ax			; Get the Current DS
	sub	bx,__psp		; and calculate DS - PSP Seg
	add	bx,si			; DS + Data length in Para's
	mov	ah,MS_M_SETBLOCK
	int	DOS_INT
	
cstart_10:
ifdef	DOSPLUS
	mov	ax,4457h		; terminate HILOAD operation
	mov	dx,200h
	int	DOS_INT
	call	dbcs_init		; initialise the DBCS support
endif
	call	get_cs
	push	ax
	mov	ax,codeOFFSET memory_init
	push	ax
	db	0CBh			; a RETF instruction to correct CS.

page
;
;	Build the full path and filename of this process using the
;	loadpath attached to the environment. If no filename exists
;	then prevent the Command Processor code from being located
;	in high memory.
;
exec_name:
	push	es
	mov	es,__psp		; Get the PSP Segment Address
	mov	dx,PSP_ENVIRON		; Get the environment segment
	cmp	dx,0000			; Have we got an environment ?
	jz	exec_n11		; No prevent High Code Support

	mov	es,dx			; Scan through the environment and
	mov	di,0			; determine the Environment size and
	mov	al,0			; the Load file name
	mov	cx,7FFFh
exec_n05:				; Scan through the Environment
	repne	scasb			; searching for the 00 00 terminator
	jcxz	exec_n10		; Terminate On CX == 0000
	cmp	es:byte ptr [di],al	; If the next byte is zero then this is
	jnz	exec_n05		; then end of the environment
	cmp	es:word ptr 1[di],1	; Are we pointing at the Control Word
	jnz	exec_n10		; No then no Load Path exists

	push	ds
	mov	ds,dx			 ; DS -> Environment Segment
	;call	get_ds
	;mov	es,ax			 ; ES -> Command Processor Data Seg
	mov	es,[low_seg]
	lea	si,03[di]		 ; Fully expand the filename so the
	mov	di,dataOFFSET reload_file ; user can SUBST drives
	mov	ah,60h
	int	DOS_INT
exec_n15:
if 0
	mov	di,dataOFFSET reload_file ; this ASCIIZ string is on the heap
	xor	ax,ax			;  so now we need to find out how
	mov	cx,-1			;  much space it takes up and reserve
	repne	scasb			;  that amount of the heap
	mov	heap_top,di		; byte after the NUL is available
endif
	pop	ds
	pop	es
	ret

exec_n10:
	mov	high_code,FALSE
exec_n11:
	mov	ah,MS_DRV_GET
	int	DOS_INT			; get default drive
	mov	es,[low_seg]
	add	es:reload_file,al	;  and use that for the comspec
	push	ds
	;call	get_ds
	;mov	es,ax			 ; ES -> Command Processor Data Seg
	jmp	exec_n15

ifdef	DOSPLUS
DI_BUF_PTR	equ	dword ptr -4	; pointer to DBCS lead byte table
DI_BUF_ID	equ	byte ptr -5	; buffer id
DI_BUF		equ	byte ptr -5	; buffer

DI_LOCALS	equ	5		; No. bytes storage local to init_dbcs

_DATA		SEGMENT byte public 'DATA'

	Public	dbcs_table_ptr

dbcs_table_ptr	label	dword
dbcs_table_off	dw	dataOFFSET dummy_dbcs_table
dbcs_table_seg	dw	0

dummy_dbcs_table dw	0

_DATA		ENDS

dbcs_init	proc	near
;--------
; To initialise the double byte character set (DBCS) lead byte table.
; MUST be called before the first call to dbcs_lead() or dbcs_expected().
; Entry
;	none
; Exit
;	none (side effect: DBCS table initialised)

	push	bp
	mov	bp, sp
	sub	sp, DI_LOCALS		; allocate local variables
	push	ds
	push	es
	push	di
	push	si

	mov	dbcs_table_seg, ds	; assume DBCS call will fail

	mov	ax, 06507h		; Extended Country Info: get DBCS ptr
	mov	bx, 0FFFFh		; codepage number: -1 for global cp
	mov	cx, 00005h		; size of info. buffer
	mov	dx, 0FFFFh		; country code: -1 for current country
	lea	di, DI_BUF[bp]
	push	ss
	pop	es			; es:di -> DI_BUF
	int	21h			; returns with DI_BUF filled in
	 jc	di_exit			; just exit if function fails

	cmp	DI_BUF_ID[bp], 7	; is table for DBCS?
	 jne	di_exit			;  no - exit
	les	ax, DI_BUF_PTR[bp]	; es:ax -> system DBCS table

	mov	dbcs_table_off,ax
	mov	dbcs_table_seg,es	; fixup pointer to system DBCS table

di_exit:
	pop	si
	pop	di
	pop	es
	pop	ds
	mov	sp, bp
	pop	bp
	ret
dbcs_init	endp

endif

; I want to do
;	db	(C_HEAP_SIZE - 6 - ($ - stack_start)) dup (0DDh)
; but MASM requires dup's be absolute, so instead
rept	C_HEAP_SIZE
if (offset $ - offset stack_start) LT (C_HEAP_SIZE - 6)
	db	0ddh
endif

endm

stack_top	label	word
stack_ip	dw	?		; Initial Offset	
stack_cs	dw	?		; Initial Code Segment (Unknown)
stack_flags	dw	?		; Initial Flags (Unknown)
STACK	ENDS

ED_TEXT	SEGMENT
;
;	Return the CheckSum of All the C code and Static Data. DS:SI 
;	are initialised to point at the C_code_start. AX contains the
;	CheckSum on return.
;
calc_crc:
	mov	cx,real_code		; Number of bytes to move is always
	shr	cx,1			; even because of the segment def.
	dec	cx
	mov	bx,0EDCh		; Checksum Seed
cc_10:					; Sum the words of the image while
	lodsw				; rotating the check value
	add	bx,ax
	rol	bx,1
	loop	cc_10
	xchg	ax,bx
	ret

page
;
;	Return the code segment of the C main routine.
;
get_cs:
	mov	ax,cs:code_seg
	ret

;
;	Return the Data segment of the C data area
;
get_ds:
	mov	ax,cs:data_seg
	ret

	
; Get remaining memory size, subtract code size and allocate high memory for
; transient portion of command processor
;	Exit	AX=Segment of high memory
;		CY set if none available
; 
; This used to allocate only just enough space for the code at the top of
; memory and leave the reset free for use as copy buffers.
; But then came SideKick Plus - this assumes COMMAND.COM only uses the top
; 20K of it's hi-mem partion and overwrites the rest as it gets it's
; overlays. As we are bigger the 20K the result is the system crashes when
; you exit back to the command line.
; In order to minimise this possibility we reserve some space (currently 50K)
; for use as copy buffers and allocate all the rest to the hi-mem portion
; of COMMAND.COM. Since SK starts overwriting from the bottom of memory it
; doesn't usually reach the code.
; The real solution is of course to make sure we are smaller than 20K too...
; (Or transfer the ReadLine code into the resident portion and checksum the
; hi-mem before returning to the hi-mem code ???).
; --ij

public	alloc_com_memory
alloc_com_memory:
	push	es
	mov	bx,0FFFFh		; Allocate more paras than there are
	mov	ah,MS_M_ALLOC		; to find out how many there are.
	int 	DOS_INT
	mov	ah,MS_M_ALLOC
	int	DOS_INT			; allocate it

	mov	si,code_length		; then convert the CODE_LENGTH to 
	mov	cl,4			; paragraphs and check if enough memory
	shr	si,cl			; is available to copy the code high
	add	si,1 			; + para for a memory descriptor 
	sub	bx,si			; Is there enough for the code ?
	 jc	acm_10			; if not exit with error


	cmp	bx,Copy_Buffer_Size	; allocate some memory for Copy Buffers
	 jb	acm_5
	mov	bx,Copy_Buffer_Size	; limit the maximum Copy Buffer size
acm_5:
	mov	es,ax
	mov	ah,MS_M_SETBLOCK
	int	DOS_INT			; shrink to fit
	push	es			; save buffer address
	
; Now allocate remaining memory for step aside code
	mov	bx,0FFFFh		; allocate all memory
	mov	ah,MS_M_ALLOC		; to find out how much there is.
	int 	DOS_INT
	mov	ah,MS_M_ALLOC		; block at the end of memory
	int	DOS_INT			; AX=Segment of new block of memory
	mov	alloc_seg,ax		; save address so we can free it
if ThreeCOM
	cmp	bx,1000h		; do we have at least 64k ?
	 jb	acm_6			; if so do 3-Com fix and start
	mov	si,1000h		; Transient portion 64k down
acm_6:
endif
	add	ax,bx			; go to top of memory we allocated

	cmp	ax,0a000h		; dont use memory above A000 as this
	jb	acm_6a			; may disappear when user does
	mov	ax,0a000h		; MEMMAX -V
acm_6a:

	dec	si
	sub	ax,si			; adjust address we run cmd proc at
; Deallocate intervening block of memory
	pop	es
	push	ax
	mov	ah,MS_M_FREE		; Now free it up
	int	DOS_INT
	pop	ax
	mov	cs:_gp_far_buff,real_code
	mov	cs:_gp_far_buff+2,ax	; save address of temp buffer
	clc				; allocated OK
acm_10:
	pop	es
	ret
	
; called to free up the cmd processor's high memory
free_com_memory:
	
	push	es
	push	ax
	mov	es,alloc_seg
	mov	ah,MS_M_FREE
	int	DOS_INT
	pop	ax
	pop	es
	ret


;Control_Break:
;	The Break handler makes the following checks taking the
;	appropriate action after each test:-
;
;	1) Is COMMAND.COM the current process (Get PSP) if NO ABORT
;	2) Is the break_flag SET if YES jump to the C break handler
;	   "break_handler" after insuring that the segment registers
;	   have all be set correctly.
;	
	assume cs:DGROUP, ds:nothing, es:nothing

;	Set up the default Control Break Handler.
handler_init:
ifndef DOSPLUS
	mov	ax,4453h			; Get the address of the
	int	DOS_INT				; internal Critical Error
	jc	handler_i10			; handler Ignore on Error
	mov	word ptr critical+0,ax		; Save the handler Offset (AX)
	mov	word ptr critical+2,bx		; and Segment (BX)
endif
	mov	al,24h				; Set the default Critical
	mov	dx,dataOFFSET critical_error	; Error Handler
	mov	bx,dataOFFSET crit_error_entry
	call	set_vector			; Setup correct ISR
	
handler_i10:
	mov	al,23h				; Set the default Control
	mov	dx,dataOFFSET control_break	; Break Handler
	mov	bx,dataOFFSET control_break_entry
;;	jmp	set_vector			; Setup correct ISR
;
;	Convert the address of the interrupt #AL service routine, whose
;	address is CS:DX to be PSP:xxxx. Five NOP must be coded after the
;	interrupt service routine for the JMPF instruction which corrects
;	the code segment.
;
set_vector:
	push	ds
	push	es
	mov	es,[low_seg]
	mov	es:byte ptr 0[bx],0EAh	; JMPF opcode
	mov	es:word ptr 1[bx],dx	; Offset of Interrupt vector
	mov	es:word ptr 3[bx],cs	; Insert the correct code seg
	mov	dx,bx
	mov	bx,es	
	sub	bx,__psp		; Calculate the correct offset for
	mov	cl,4			; the interrupt handler if the segment
	shl	bx,cl			; must be that of our PSP
	add	dx,bx
	mov	ds,__psp
	mov	ah,MS_S_SETINT
	int	DOS_INT
	pop	es
	pop	ds
	ret

	;extrn	_int_break:near			; C Break/Error handling routine 

	assume cs:DGROUP, ds:DGROUP, es:nothing

control_break	PROC	FAR
	;;db	5 dup(90h)		; Reserve space for JMPF CS:$+5
	push	ax			; Save the Users registers
	push	bx
	push	ds
	call	get_ds			; Get our Local DS
	mov	ds,ax
	cmp	_cbreak_ok,0		; is our handler initialised ?
	 je	break_05		;  no, don't call it
	cmp	reload_flag,0		; Are we part way through reloading
	clc				;  the command processor?
	 jnz	break_05		; if so do not abort.

	mov	ah, MS_P_GETPSP		; Get the current PSP address
	int	DOS_INT
	cmp	bx,[__psp]		; Is this our address
	stc				; Assume not and Set the carry flag
	jz	break_10		; if internal, restart the command loop
					; if external, abort process
break_05:
	pop	ds
	pop	bx
	pop	ax
	ret

;
;	This section of code corrects the stack and jumps to the 
;	C code Control Break Handler _int_break. Beware that the
;	stack segment need not be the DS because the READ_LINE routine
;	executes on a high stack and the CED programs will emulates
;	a Control-Break on the wrong Stack.
;
break_10:
	cli				; Swap to the correct stack not
	mov	ax,ds			; forgetting that we might be running
	mov	es,ax			; on old 8088 or 8086 so interrupts
	mov	ss,ax			; must be disabled
	mov	sp,stack_min		; Get the lowest possible stack address
	add	sp,12			; Add a little to avoid problems with
	sti				; the stack check code.

	mov	ax,0100h		; Termination Code Control-C Abort
	push	ax			; Save on the Stack for C routine
	push	ax			; Force a dummy return address on the
					; stack (NOT USED)
	call	get_cs			; Put the Segment and Offset address
	push	ax			; of the C break Handler on the stack
	mov	ax,codeOFFSET _int_break; and execute a RETF to it.
	push	ax
	ret 
control_break	ENDP

critical_error	PROC FAR
	;;db	5 dup(90h)		; Reserve space for JMPF CS:$+5
	push	ds			; Save the Critical Error DS
	push	ax
	call	get_ds			; Get the Command Processor DS
	mov		ds,ax			; and call the original routine
ifdef DOSPLUS
	mov 	ax, _n_option	; check for /n command line option
	cmp		ax, 0
	pop		ax
	jne		skip_criterr
	call	com_criterr		; local criterr handler
	jmp		criterr_cont
skip_criterr:
	mov		al,3			; /n option => always return fail
criterr_cont:
else
	pop		ax
	pushf
	call	critical		; stored previous handler
endif
	cmp	al,02h			; Did the user request a Terminate
	 jne	critical_e20		; Yes so check if they are trying to
	push	ax			; terminate the command processor
	push	bx

	mov	ah, MS_P_GETPSP		; Get the current PSP address
	int	DOS_INT
	cmp	bx,[__psp]		; Is this our address?
	 jne	critical_e10		; no so return with Abort code

	cmp	in_exec,0		; are we EXECing a program ?
	 jne	critical_e10		;  then return the error
	cmp	reload_flag,0		; then unless we are reloading command
	 je	break_10		;  processor break here to prevent
critical_e10:				;  higher levels generating repeated
	pop	bx			;  critical errors (eg. when searching
	pop	ax			;  a path)
critical_e20:
	pop	ds
	iret

critical_error ENDP

;
;	INT2E is a backdoor entry to the "Permanent" Command interpreter 
;	which will execute the command at DS:SI.
;

int2E_far_entry	proc	far
	db	5 dup(90h)		; Reserve space for JMPF CS:$+5

;	ds:si -> command line preceded by byte count

;	check we're not re-entering

	mov	ax, 1
	xchg	cs:i2e_lock, ax
	test	ax, ax
	 jz	i2e_10

	iret

i2e_10:
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es
	
	; if ds = si = 0 then set batch_seg_ptr to zero and get out.
	; This is a clean way of halting batch processing.
	mov	ax,ds
	cmp	ax,0
	jne	i2e_15
	cmp	si,0
	jne	i2e_15

	mov	si,cs:_batch_seg_ptr
	mov	ds,cs:_batch_seg_ptr+2
	mov	[si],ax
	jmp	i2e_exit2
i2e_15:	

;	swap stack

	mov	cs:i2e_user_ss, ss
	mov	cs:i2e_user_sp, sp
	
	cli
	mov	ss, cs:exec_ss
	mov	sp, cs:exec_sp
	sti
	
;	allocate 128 bytes for command line, since the C code needs a 128 
;	byte buffer anyway

	mov	cx, 128

;	save stack p

	mov	cs:i2e_stack, sp

;	have we got enough room for command line 

	mov	ax, sp
	sub	ax, cx
	sub	ax, cs:heap_top
	cmp	ax, 256
	 jge	i2e_25
	jmp	i2e_exit
i2e_25:	
	sub	sp, cx
	mov	cs:i2e_cmd, sp
	
;	copy command line

	push	ss
	pop	es
	mov	di, sp
	lodsb				; get line count
	and	ax,7fh			; limit to 128 bytes
	add	ax,sp			; terminating NULL goes here
	rep	movsb			; copy the command line
	xchg	ax,di			; DI -> NUL posn
	xor	al,al
	stosb				; replace CR with NUL
	push	cs
	pop	ds

;	reload non-resident code if necessary

	cmp	high_code, TRUE
	 jnz	i2e_30
	mov	reload_flag, 1
	call	reload_code
	mov	reload_flag, 0

i2e_30:
;	install our own Break and Criterr handlers - e.g. if second copy
;	of the command processor is running, install original handlers so
;	that they are looking at the same data as we are.
;	N.B. __psp is currently that of original command processor

	mov	ax, (MS_S_GETINT*256) + 23h
	int	DOS_INT
	mov	i2e_i23seg, es
	mov	i2e_i23off, bx
	
	mov	ax, (MS_S_GETINT*256) + 24h
	int	DOS_INT
	mov	i2e_i24seg, es
	mov	i2e_i24off, bx
	
	call	handler_init
	
;	save the command processor's __psp variable, and then set it to the
;	psp of the process that called us. This is so that the Break and 
;	Criterr abort code can correctly determine whether the int2e command 
;	was internal or external

	mov	ah, MS_P_GETPSP
	int	DOS_INT
	push	bx		; save calling process's psp - EJH	
	mov	bx, [__psp]

; Set current PSP to our own - EJH	
	mov	ah, MS_P_SETPSP
	int	DOS_INT
	
;	call C code

	mov	ax, code_seg
	mov	i2e_c_seg, ax
	
	push	i2e_cmd	
	call	i2e_c_entry
	pop	ax				; clean up stack
	
; Set current psp back to that of calling process - EJH
	pop	bx
	mov	ah, MS_P_SETPSP
	int	DOS_INT


;	restore interrupt vecs

	push	ds
	lds	dx, i2e_i23vec
	mov	ax, (MS_S_SETINT*256) + 23h
	int	DOS_INT
	
	lds	dx, cs:i2e_i24vec
	mov	ax, (MS_S_SETINT*256) + 24h
	int	DOS_INT
	pop	ds
	
	cmp	high_code, TRUE			; If not a .EXE then free
	 jnz	i2e_ret				; any memory alocated by
	call	free_com_memory			; the Command Processor
i2e_ret:

i2e_exit:
;	swap back the stack

	cli
	mov	ss, i2e_user_ss
	mov	sp, i2e_user_sp
	sti

i2e_exit2:
	pop	es
	pop	ds
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx

    sub ax, ax          

	mov	cs:i2e_lock, 0
	ret	2

int2E_far_entry	endp


_TEXT	SEGMENT
	assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing
	public	_exec			; EXEC routine

;
; WORD CDECL exec(BYTE *path, UWORD type, BYTE *line, BOOLEAN back);
;
;   back    10[bp]      
;	line	08[bp]
;	type	06[bp]
;	path	04[bp]
;
_exec:
;-----
	push	bp
	mov	bp,sp
	push	si
	push	di
	push	ds
	push	es
    inc in_exec         
    cmp in_exec,1       
	 je	_exec10			; Menuing system
	push	exec_ss			; save old stack_save if we
	push	exec_sp			;  are being re-entered
_exec10:
	mov	si,08[bp]		; the Command Line
	mov	exec_env,0000
	mov	exec_clineoff,si
	mov	exec_clineseg,ds
	mov	exec_fcb1seg,ds
	mov	exec_fcb2seg,ds

	push	ds
	pop	es
;
;	Extract two valid filenames from the CR terminated
;	string passed in DS:SI. The FCBs will be generated in FCB1 and FCB2.
;
	inc	si
	mov	di,dataOFFSET fcb1	; Blank fill the first FCB
	call	zap_fcb
	call	make_fcb		; Build first FCB

	mov	di,dataOFFSET fcb2	; Blank fill the Second FCB
	call	zap_fcb
	call	make_fcb		; Build second FCB

	mov	ax,cs
	jmp	exec			; do FAR jmp to msdos_exec as we can't
_exec_ret:				;  trust the stack when CALLing
	dec	in_exec			; Has PCTOOLS un-installed itself ?
	 jz	_exec20			; No so the stack contents are valid
	 js	_exec_bad		; Yes, so exit by Ctrl-Break
	pop	exec_sp			; old stack save back again
	pop	exec_ss			;  in the case of re-entry
_exec20:
	
	pop	es
	pop	ds
	pop	di
	pop	si
	pop	bp
	mov	ax,return_code
	neg	ax
	ret
;
;	We have returned from the EXEC function un-expectedly (because
;	of PCTOOLS De-installation ?). Therefore the contents of the stack
;	are invalid and we generate a Control-Break.
;
_exec_bad:
	mov	ax,0100h		; Termination Code Control-C Abort
	push	ax			; Save on the Stack for C routine
	push	ax			; Force a dummy return address on stack
	mov	in_exec,al		; zero in_exec count for next time

	extrn	_int_break:near		; C Break/Error handling routine 
	jmp	_int_break		; now treat as ctrl-break

;
;	Initialise the FCB entry a DX:DI and copy the Drive, FileName
;	and extension form DS:SI when SI != 0FFFFh
;
zap_fcb:
	mov	ax,2000h		; Zero Fill first byte and last 4
	stosb				; Drive code 0
	xchg	al,ah
	mov	cx,11			; FileName and Ext to ' '
	rep	stosb
	xchg	al,ah
	mov	cl,4			; Last four bytes to 00
	rep	stosb
	sub	di,16
	ret

make_fcb:
	call	scan_filechar		; Get the Next File Character
	push	si
	push	di
	push	ax			; Save Character and Pointers

	mov	ax,(MS_F_PARSE shl 8) + 1 ; Parse the command line
	int	DOS_INT

	pop	ax
	pop	di			; Restore the Character and Pointer

	cmp	ah,'/'			; Was the arg preceeded by a '/'
	jne	make_fcb10		; If it was roll back the cmd line 
	pop	si			; pointer and blank all but the 1st
	inc	si			; char in the FCB

	add	di,2			; Such that /FRED gives an FCB of 'F'
	mov	cx,10			; and si points to the 'R'	
	mov	al,' '
	rep	stosb
	ret

make_fcb10:
	pop	ax			; discard original SI, skip name
	jmp	scan_sepchar		; skip until separator

;
;	SCAN_SEPCHAR will search through the command line DS:SI
;	which contains CX characters and return with SI pointing
;	to the next SEPARATOR character.
;
;	On Entry:	DS:SI	Character String
;
;	On Exit:	DS:SI	First Non Separator
;
scan_sepchar:
	push	di			; Save DI  ES points at this segment
	mov	ah,0			; Invalidate Separator Character

scan_s10:
	mov	al,[si]			; Get the Character to Test
	cmp	al,CR
	jz	scan_s20		; Have we reached the end of the string
	mov	cx,legal_length			; Scan the table of legal 
	mov	di,dataOFFSET legal_table	; separators
	repnz	scasb				; Scan the List
	jz	scan_s20			; Separator Located
	inc	si				; the character pointer
	jmp SHORT scan_s10

scan_s20:	
	pop	di
	ret
;
;	SCAN_FILECHAR will search through the command line DS:SI
;	and return with SI pointing to the next NON_SEPARATOR character.
;
;	On Entry:	DS:SI	Character String
;
;	On Exit:	DS:SI	First Non Separator
;			AH	Last Valid Separator
;
scan_filechar:
	push	di			; Save DI  ES points at this segment
	mov	ah,0			; Invalidate Separator Character
scan_f10:
	mov	al,[si]			; Get the Character to Test
	cmp	al,CR
	jz	scan_f20		; Have we reached the end of the string
	mov	cx,legal_length			; Scan the table of legal 
	mov	di,dataOFFSET legal_table	; separators
	repnz	scasb				; Scan the List
	jnz	scan_f20			; Non Separator Located
	mov	ah,al				; Save the Separator and increment
	inc	si				; the character pointer
	jmp SHORT scan_f10

scan_f20:	
	pop	di
	ret

_TEXT	ENDS

legal_table	db	':.;,=+',9,' /<>|',22h,'[]'	
legal_length	equ	15

	assume cs:DGROUP, ds:DGROUP, es:nothing, ss:nothing

	public	msdos_exec	; so it shows in map file
	
msdos_exec PROC FAR
	push	ax
	cmp	high_code,TRUE		; Check if the Command Processor Code
	jnz	msdos_e10		; has been relocated to high memory

	call	free_com_memory		; free cmd processor memory

msdos_e10:
	pop	ax
	mov	bx,dataOFFSET exec_block
	mov	dx,04[bp]		; Get the Full Command Name
	mov	exec_ss,ss		; Save SS:SP in case somebody (NOVELL)
	mov	exec_sp,sp		; corrupts them

;	The following 2 JMPF are kludged so that the INT22 vector saved in
;	the child's PSP has the segment of the comand processors PSP.
;	This is vital for many TSR management utilities.
;

					; swap stack to conventional memory
	cli				;
	mov	ax,__psp		;
	mov	ss,ax			;
	mov	sp,0100h		;
	sti				;

		db	0EAh			; JMPF Opcode
		dw	dataOFFSET psp_dofunc4b	; Corrected Offset
exec_psp	dw	0			; PSP Segment

	assume cs:DGROUP, ds:nothing, es:nothing, ss:nothing
func4b_return:
	mov	ss,exec_ss		; Restore the real SS:SP
	mov	sp,exec_sp		; Using a CS overide 
	 jc	msdos_e20		; if no error
	xor	ax,ax			; then zero return code
	jmp	msdos_e21
msdos_e20:
	mov	ah,59h			; get extended error code
	sub	bx,bx
	int	21h
msdos_e21:
	mov	return_code,ax
	call	get_ds			; Point DS & ES to CSdata
	mov	ds,ax
	mov	es,ax
	assume cs:DGROUP, ds:DGROUP, es:nothing, ss:nothing

	call	handler_init		; Re-initialise Control-Break and
					; Critical error handlers for Novell
	cmp	high_code,TRUE		; Is the Command Code in High
	 jnz	msdos_e30		; memory ?
	mov	reload_flag,1		; Reloading Command Processor
	call	reload_code		; Reload Command Processor Code
	mov	reload_flag,0		; Command Processor Loaded
msdos_e30:
	mov	ax,code_seg		; Update the CS for the high code
	mov 	msdos_exec_ret_seg,ax	; in case it has moved and return
	jmp	msdos_exec_ret

msdos_exec ENDP

_TEXT	SEGMENT
	assume cs:CGROUP, ds:DGROUP, es:nothing, ss:nothing

	Public	_readline
	
_readline:
;---------
	cld
	push	bp
	mov	bp,sp
	push	si
	push	di
	push	es
ifdef	DOSPLUS
	mov	ax,4456h		; Swap to the Command process
	mov	dl,1			; History Buffer in DR DOS
	int	DOS_INT
else
	push	ds
	call	get_history_buffers	; get history buffers
	mov	P_SB_SEG[bx],ax		; swap to command one
	pop	ds
endif
	mov	ax,5d09h
	int	DOS_INT			; close remote spool files
	mov	ax,5d08h
	mov	dl,1
	int	DOS_INT			; Set truncate flag with redirected I/O

	mov	ah,MS_P_GETPSP		; get the current PSP
	int	DOS_INT			;  then compare the STDERR entry in the
	mov	es,bx			;  XFT with the Magic Number for CON
	cmp	es:byte ptr [18h+STDERR],1
	mov	dx,4[bp]		; get the buffer address and current
	mov	ax,cs			; AX = transient code segment
	call	readline		; do far call to msdos_readline
ifdef	DOSPLUS
	mov	ax,4456h		; Swap to the Application process
	mov	dl,0			; History Buffer in DR DOS
	int	DOS_INT
else
	push	ds
	call	get_history_buffers	; get history buffers
	mov	P_SB_SEG[bx],dx		; swap to application one
	pop	ds
endif
	pop	es
	pop	di
	pop	si
	pop	bp
	ret

ifdef CDOS
get_history_buffers	proc	near
; On Entry:
;	None
; On Exit:
;	AX = command history buffers
;	DX = application history buffers
;	DS:BX -> pd
;	NB. both DS and ES are corrupted by this call
;
	mov	ds,_sysdat_seg		; DS points to SYSDAT and ES to the
	mov	bx,ds:word ptr [68h]	; current process's UDA
	mov	dl,P_CNS[bx]		; DL = console number
	mov	es,P_UDA[bx]
	push	bx
	mov	ax,XIOS_HISTBUF		; Get the History Buffer Address's
	call	XIOS_ENTRY		;  by calling ths XIOS
	pop	bx
	ret
get_history_buffers	endp
endif

_TEXT	ENDS

	assume cs:DGROUP, ds:DGROUP, es:nothing

;
; msdos_readline(BYTE *buffer);
;
; On Entry:
;	flags - ZF set if we need to do Sidekick Plus check
;	DS:DX -> buffer
;	AX:0  -> Resident Section
;	buffer
;
; The memory allocation for the hi-mem part of command.com has already been
; altered to minimise the chances of SideKick Plus overwriting the code.
; But it still can happen, say either on a machine with limited memory or
; other TSR's also loaded. In order to cater for this case we move the actual
; readline call into the resident portion and checksum things before returning
; to high memory - thus we can reload command.com if it get overwritten.
;
msdos_readline PROC FAR
	pushf				; save the result for later

	mov	bx,sp			; SP before swapping stacks.
	cli				; to RLSTACK
	mov	ss,ax			; above the Code and Messages
	mov	sp,codeOFFSET rlstack
	sti
	
	push	bx			; save old SP
	push	ds

	sub	sp,260			; make room for buffer on stack
	mov	di,sp			; save the address

	push	ds
	push	dx			; save real BUFFER for later...
	push	di			;  and buffer on stack

	mov	si,dx			; point DS:SI at real buffer
	mov	dx,di			; point DX at stack buffer
	push	ss			; now get all the seg regs
	pop	es			; pointing to himem
	mov	cx,256/2
	rep	movsw			; copy resident buffer to stack
	push	ss			; DS:DX is stack buffer
	pop	ds

	mov	ax,4810h		; give DOSKEY a chance
	int	2Fh
	or	ax,ax			; zero means DOSKEY has done it
	 jz	msdos_rl10

	mov	word ptr [di],21cdh	; poke INT 21 and
	mov	byte ptr 2[di],0cbh	;  RETF instruction
	push	cs			; save an address for the
	mov	ax,dataOFFSET msdos_rl10;  RETF to return to
	push	ax

	mov	ah, MS_C_READSTR	; parameter for INT 21 readline

	push	ds			; finally jump to the INT 21
	push	di			;  we have poked
    ret             

msdos_rl10:
	pop	si			; source is readline buffer
	pop	di			; recover old address
	pop	es			;  and seg of real buffer

	mov	cl,ds:byte ptr [si]	; get MAX length
	xor	ch,ch			; make it a word
	cld
	rep	movsb			; copy the string

	add	sp,260			; recover my readline buffer space
	pop	ds
	pop	bx			; recover old SP

	cli				; Now restore the normal C stack
	call	get_ds			; which is at DS:BX.
	mov	ss,ax
	mov	sp,bx
	mov	es,ax
	sti

	popf				; recover result of STDERR test
	 jne	msdos_r10		; if not CON, then skip the check
	cmp	high_code,TRUE		; Is the Command Code in High
	 jnz	msdos_r10		; memory ?
	mov	reload_flag,1		; Reloading Command Processor
	call	reload_code		; Reload Command Processor Code
	mov	reload_flag,0		; Command Processor Loaded
msdos_r10:
	mov	ax,code_seg		; Update the CS for the high code
	mov 	word ptr -08[bp],ax	; in case it has moved and return
	ret
msdos_readline ENDP

; Cmd processor is in high memory so check it out
reload_code:
	cmp	in_exec,1	; If we landed here from an undefined area
	je	alloc_mem	; then free cmd processor memory
	call	free_com_memory

alloc_mem:
	call	alloc_com_memory	; Allocate memory for the Command
	 jc	alloc_mem_error		; processor code.
	push	ax
	call	check_crc		; see if code has been trashed
	pop	ax
	je	still_there
	mov	code_seg,ax
	jmp	load_com		; Read in command Processor Code

still_there:
	cmp	ax,code_seg		; has the memory configuration changed?
	je	hasnt_moved		; (it will during Novell remote boot)

	push	ds
	push	es

	mov	cx,real_code
	mov	es,ax			; move the code to the new location
	mov	ds,code_seg		;
	sub	si,si			; ds:si -> old location
	sub	di,di			; es:di -> new location

	rep	movsb			; do it

	mov	cs:code_seg,es		; update code_seg with new location
	
	pop	es
	pop	ds

	call	check_crc		; recalculate the crc
	jne	load_com		; if its not valid we need to load
					; from disk

hasnt_moved:
	ret

alloc_mem_error:
; We can't reload COMMAND.COM because we haven't any memory
ifdef DLS
	mov	dx,_reload_msgm
else
	mov	dx,dataOFFSET _reload_msgm
endif
	call	reload_err		; say we can't reload COMMAND.COM
	jmp	$			; stop forever....

check_crc:
; return with ZF set if himem crc's correctly
	push	ds
	mov	ds,code_seg
	mov	si,2
	call	calc_crc
	pop	ds		; Compare this crc with the one we got earlier
	cmp	crc,ax
	ret
	
; If checksum is wrong load a new copy into high memory and patch new RET addr

open_err:			
ifdef DLS
	mov	dx,_reload_msgf
else
	mov	dx,dataOFFSET _reload_msgf
endif
	call	reload_err		; prompt for COMMAND.COM
	mov	ah,MS_C_RAWIN		; Wait for the User to Press
	int	DOS_INT			; a key

load_com:
	push	ds
	mov	ds,[low_seg]
	mov	dx,dataOFFSET reload_file
	mov	ax,(MS_X_OPEN*256)+40h	; Try to open COMMAND.COM
	int	DOS_INT			; al=40h means NONE DENIED share mode
	pop	ds
	 jc	open_err
	mov	bx,ax

	xor	dx,dx			; Take account of EXE header
	cmp	exe_file,FALSE		; if we were loaded as an EXE
	je	load_com_10
	add	dx,200h			; Assume header is 200h bytes

load_com_10:
	xor	cx,cx			; Now read the command processor code
	add	dx,total_length		; Seek to area of COMMAND.COM
	mov	ax,(MS_X_LSEEK*256)+0	; containing code
	int	DOS_INT
	 jc	read_err

	push	ds
	mov	cx,real_code
	mov	ds,code_seg		; DS:DX - > High memory area
	xor	dx,dx
	mov	ah,MS_X_READ		; Read the code into memory
	int	DOS_INT

	pop	ds
read_err:
	mov	ah,MS_X_CLOSE		; Close the file
	int	DOS_INT

	call	check_crc		; try and see if we loaded OK
	 jne	open_err		; if not complain and try again

	ret

reload_err:
; On Entry:
;	DX = offset of final part of error message
;
	push	dx				; save final part of message
	mov	bx,STDERR			; Display on standard Error
ifdef DLS
	mov	dx,_reload_msgs	
else
	mov	dx,dataOFFSET _reload_msgs	
endif
	call	reload_err10

	push	ds
	mov	ds,[low_seg]
	mov	dx,dataOFFSET reload_file	; Filename
	call	reload_err10
	pop	ds

	pop	dx				; recover msg offset
;
;	Calculate the length of the message to be displayed and output using
;	MS_X_WRITE.
;
;	DS:DX		String to Output
;	BX		Output Handle
;
reload_err10:
	xor	al,al				; String Terminator
	push	es
	push	ds				; look for terminating BYTE
	pop	es				;  in string at DS:DX
	mov	di,dx				;  and get length to display
	mov	cx,0ffffh
	repnz	scasb				; look for terminator
	not	cx				; see how far we had to look
	dec	cx				; forget about the terminator
	mov	ah,MS_X_WRITE
	int	DOS_INT
	pop	es
	ret

ED_TEXT	ENDS
endif

	page
_TEXT	SEGMENT
	assume cs:CGROUP, ds:DGROUP, ss:DGROUP
ifdef CDOSTMP
RSP_start:
	mov	ax,ds
	mov	es,ax
	cli				; Swap stacks with interrupts disabled
	mov	ss,ax			; just in case we're on an 8088/86
	mov	sp,dataOFFSET stack_top
	sti
	mov	insys,0			; Reset the INSYS Flag

	mov	_gp_far_buff,dataOFFSET temp_buffer
	mov	_gp_far_buff+2,ax	; save address of temp buffer

	mov	dl,defconsole		; Set this process's Default Console
	bdos	C_SET
	bdos	C_ATTACH

	bdos	P_PDADR			; Get the address of the current
	mov	sysdat,es		; PD and SYSDAT

	mov	dl,BOOTDRV		; Get the BOOT Drive
	mov	es:P_DSK[bx],dl		; Update the running process
	mov	bx,dataOFFSET pd_seg	; Update the default drive field
	mov	P_DSK[bx],dl		; In the P_CREATE process descriptor

	mov	bx,es:INT17_PTR		; Initialise the default printer
	mov	al,defconsole		; from the INT17 Mapping Array
	mul	es:byte ptr [bx]
	add	bx,ax
	mov	dl,es:1[bx]
	bdos	L_SET

	mov	bx,es:INT14_PTR		; Initialise the default printer
	mov	al,defconsole		; from the INT14 Mapping Array
	mul	es:byte ptr [bx]
	add	bx,ax
	mov	dl,es:1[bx]
	bdos	A_SET

	mov	[code_seg],cs		; Update the CODE_SEG and EXEC_SEG
	mov	[exec_seg],ds		;     variables

	mov	dl,0FFh			; Set the BDOS Error Mode to Return
	bdos	F_ERRMODE		; with No Display

	mov	dx,ds			; Initialise the DMA Segment
	bdos	F_DMASEG		; Pointer to Our DS

	bdos	P_PDADR			; Set ES:BX to the Process Descriptor
	mov	word ptr _pd,bx		; Save these values locally
	mov	word ptr _pd+2,es
	mov	_sysdat_seg,es

	or	es:P_CONMODE[bx],PCM_FCTLC
	mov	es:P_PARENT[bx],0	; Zero the Parent Pointer
	mov	ax,es:P_UDA[bx]		; Finally get our UDA and save it. 
	mov	uda,ax
	
	
	mov	ax,es:P_PSP[bx]
	mov	[__psp],ax
	
	mov	ax,XIOS_HISTBUF		; Get the History Buffer Address's
	mov	dl,defconsole		; for console number
	call	xios
	mov	cmd_histbuf,ax		; AX == Command Level History Buffer
	mov	prog_histbuf,dx		; DX == Program History Buffer

	les	bx,_pd			; Get the Process descriptor address
	mov	es:P_SB_SEG[bx],ax	; and use the Command Line 
					; History Buffer

; File Ownership stuff
	les	bx,_pd			; Get our process descriptor address
	mov	al,es:P_CNS[bx]		; get the current VC No.
	xor	ah,ah
	mov	bx,CCBLIST		; XIOS CCB$LIST
	add	bx,ax
	add	bx,ax
	mov	bx,es:word ptr [bx]	; extract the correct
	mov	ah,CCB_PCNS[bx]		; Physical Console Number.

	mov	ch,0
	mov	cl,NVCNS		; now work through all consoles
	mov	bx,CCBLIST		; looking for 1st one with this PC
RSP_start10:
	mov	di,es:word ptr [bx]	; Get the CCB address
	cmp	ah,CCB_PCNS[di]		; Is this the same physical console
	 je	RSP_start20		; no, ignore this one
	add	bx,2			; onto next CCB
	loop	RSP_start10
	jmp	RSP_start60		; impossible....

RSP_start20:
	cmp	al,CCB_VCNS[di]		; is it the same virtual console ?
	 je	RSP_start40		;  yes, allocate new FA_ structure
RSP_start30:
	push	di
	mov	dx,5
	bdos	P_DELAY			; wait a bit
	pop	di			; then see if it has been allocated
	mov	si,CCB_OWNER[di]	; si -> owning process
	mov	si,es:P_FILE_ACCESS[si]	; File Access structure
	test	es:FA_FLAGS[si],FAF_TMP_INIT ; is it the new one ?
	 jz	RSP_start30		; no, delay again
	inc	es:FA_COUNT[si]		; one more user for this structure
	les	bx,_pd
	xchg	si,es:P_FILE_ACCESS[bx]	; replace existing one
	dec	es:FA_COUNT[si]		; one less user for old one
	jmp	RSP_start60

RSP_start40:
	push	ax			; save PC/VC
	cmp	ah,0			; is it the main box ?
	 je	RSP_start45
	mov	dx,5*50			; delay PC Terminals 5 secs while
	bdos	P_DELAY			;  main box starts up
RSP_start45:
	mov	DX,FA_LENGTH		; we need this much SYSDAT memory
	bdos	S_MEMORY		;  for our file access structure
	pop	dx			; recover PC/VC
	cmp	ax,0FFFFh		; if no memory
	 je	RSP_start50		;  then just keep root one
	les	di,_pd			; replace file access structure
	xchg	ax,es:P_FILE_ACCESS[di]
	xchg	ax,di
	dec	es:FA_COUNT[di]		; we have stopped using this structure
	mov	es:FA_USER[bx],0	; user # is super user
	mov	es:FA_GROUP[bx],0	; and group is super group
	mov	es:FA_DEF_ACCESS[bx],0	; initialise the new structure
	mov	es:FA_COUNT[bx],1
RSP_start50:
	les	bx,_pd
	mov	bx,es:P_FILE_ACCESS[bx]
	mov	es:FA_FLAGS[bx],FAF_TMP_INIT
RSP_start60:
; File Ownership stuff ends

	push	ds
	mov	es,sysdat		; Get SYSDAT
	mov	dx,VERSION		; get OS label offset in SUP segment
	mov	ds,CCPMSEG		; get SUP segment for signon string
	bdos	C_WRITESTR		; print OS label on current console
	pop	ds
endif
;
;
	assume cs:CGROUP, ds:DGROUP, es:DGROUP
;
;	zero data areas (_BSS and c_common)
;
memory_init:
	push	ss
	pop	es

	cld				; set direction flag (up)
	mov	di,dataOFFSET _edata	; beginning of bss area
	mov	cx,dataOFFSET _end	; end of bss area
	sub	cx,di
	xor	ax,ax
	rep	stosb			; zero bss

;	C segmentation conventions set up here	(DS=SS and CLD)

	push	ss			; set up initial DS=ES=SS, CLD
	pop	ds
	assume ds:DGROUP

;	do necessary initialization BEFORE command line processing !

ifndef CDOSTMP
	push	es
	mov	ax,4458h		; We now have an IOCTL function
	int	21h			;  to get our private data
     jc mem_init10  
ifdef DOSPLUS       
	;les	ax,DRDOS_PD
	;les	ax,es:dword ptr 0000h[bx]
	;mov	word ptr _pd,ax	
	;mov	word ptr _pd+2,es
	;mov	_sysdat_seg,es
else
	mov	es:byte ptr [bx],-1	; say COMSPEC is loaded
	mov	cl,P_PDADR		; Set ES:BX to the Process Descriptor
	mov	ax,4459h		; use Int 21h so we don't crash if
	int	21h			;  under DOS and we will give an error
	mov	word ptr _pd,bx		;  from COM.C init().
	mov	word ptr _pd+2,es	; Save these values locally
	mov	_sysdat_seg,es
endif
mem_init10:
	pop	es
endif
	xor	bp,bp			; mark top stack frame for SYMDEB

ifdef CDOSTMP
	mov	ax,dataOFFSET cmdline	; Pass a pointer to a NULL string
else
	call	get_cmdline		; Copy the command line to a local
endif
	push	ax			; buffer and pass the address to MAIN
	call	C_code_entry		; main ( cmd )
	add	sp,2			; Restore the Stack
	mov	ah,MS_X_EXIT		; use whatever is in ax after 
	int	DOS_INT			; returning here from main

page
;
;	For TURBOC and METAWARE C the majority of the Command Processor
;	messages are linked with the CGROUP in the MSG segment. When the
;	compiler generates DGROUP relative references large offsets are
;	produced. This occurs because the DGROUP is linked before the CGROUP
;	and its MSG Segment. FARPTR converts a NEAR pointer into a FAR
;	by checking the value of the offset against the DGROUP length.
;	Offset greater than TOTAL_LENGTH must be in MSG so the pointer
;	is converted to CS:x-TOTAL_LENGTH otherwise DS:x.
;
	public	_farptr
_farptr:
	push	bp
	mov	bp,sp
	mov	ax,04[bp]
	mov	dx,ds
	cmp	ax,total_length
	jbe	farptr_10
ifndef WATCOMC
	sub	ax,total_length
endif
	mov	dx,cs
farptr_10:
	mov	bx,dx
	pop	bp
	ret

ifdef DLS
	public	_msgfarptr
_msgfarptr:
	push	bp
	mov	bp,sp
	mov	ax,04[bp]
	add	ax,offset CGROUP:_MSG
	mov	dx,cs
	mov	bx,dx
	pop	bp
	ret
endif

; We need some additional support for the CMD_LIST - it contains near pointers
; to other items in the CGROUP. This routine turns a NEAR pointer into a FAR
; pointer.
	public	_cgroupptr
_cgroupptr:
	push	bp
	mov	bp,sp
	mov	ax,04[bp]
	mov	dx,cs
	mov	bx,dx
	pop	bp
	ret

ifdef DLS

;/* Get the address of the msg_language variable in the DR BDOS. */
;GLOBAL BYTE FAR	* CDECL get_msg_lang()

	public	_get_msg_lang
_get_msg_lang:
	mov	ax, 4458h		; IOCTL func: get ptr private data
	int	21h			; pointer returned in ES:BX
	 jc	_get_msg_lang_err	; skip if invalid function
	add	bx, 9			; DLS version byte at offset 9
	cmp	es:byte ptr [bx], 1	; correct version?
	 jne	_get_msg_lang_err	;  no - skip
	inc	bx			; DLS language byte at offset 10
	mov	ax,bx
	mov	dx,es			; return pointer in DX:AX
	ret

_get_msg_lang_err:
	mov	ax,codeOFFSET dummy_lang
	mov	dx,cs
	ret

dummy_lang	db	0		; default to primary language

endif

page
ifndef	CDOSTMP

;
;	This following routine copies the initial command line from the PSP
;	into the CMDLINE data area. Processing any special switches and
;	adding a terminating null character. The offset of the copy is
;	returned in AX.
;

get_cmdline:
if 0
	mov	di,dataOFFSET cmdline
else
	mov	di,heap_top			; copy cmdline onto the heap
endif
	push	ds				; Preserve DS and point to PSP
	mov	ds,__psp			; Get the PSP address
	xor	cx,cx				; Now copy the command line
	mov	cl,ds:0080h			; Get the Command length
	mov	si,0081h			; and its start location
	 jcxz	get_cmdl20
get_cmdl10:
	lodsb					; Terminate the copy after
	cmp	al,0Dh				; CX bytes or earlier if a
	jz	get_cmdl20			; CR or LF character is found.
	cmp	al,0Ah				; FrameWork Install Program and
	jz	get_cmdl20			; Bitstream FontWare
	stosb
	loop	get_cmdl10

get_cmdl20:	
	xor	al,al				; Zero Terminate the command
	stosb					; line copy for C.
	pop	ds
if 0
	mov	ax,dataOFFSET cmdline		; Return the command line 
else
	mov	ax,di				; new bottom of heap
	xchg	ax,heap_top			; return old one = cmdline
endif
	ret

;
;	This routine will install the DS relative dummy INT 2E handler with
;	a PSP:XXXX entry in the interrupt vector table. A JMPF is coded
;	after the handler entry point inorder to correct CS.
;
	Public	_install_perm
_install_perm:
	push	es
	mov	ax,__psp			; Modify the INT 22 and 2E
	mov	es,ax				; vectors if the current process
	cmp	ax,PSP_PARENT			; is the ROOT DOS process. ie
	jnz	inst_p10			; PSP_PARENT == PSP

	mov	al,2Eh				; Install the Command Processor
	mov	dx,dataOFFSET int2E_entry	; Backdoor entry
	call	inst_p30

	mov	al,22h				; Update the Command Processor
	mov	dx,dataOFFSET int22_entry	; Terminate address
	call	inst_p30

	push	ds				; When this is the ROOT process
	push	si				; then update the PSP copies of
	push	di				; interrupt vectors 22, 23, 24
	push	ds				; because some TSR management
	pop	es				; programs examine these variables
	lea	di,psp_save_area		; ES:DI -> save area for PSP
	mov	ds,__psp
	lea	si,PSP_TERM_IP			; DS:SI -> data in PSP to save
	mov	cx,6
	rep	movsw				; save the data for later EXIT
	push	ds
	pop	es
	lea	di,PSP_TERM_IP			; ES:DI -> PSP
	mov	ds,cx
	mov	si,022h * 4			; DS:SI	-> real interrupt vecs
	mov	cx,6
	rep	movsw
	pop	di
	pop	si
	pop	ds

inst_p10:
	pop	es
	ret

inst_p30:
	push	ds
	mov	bx,[low_seg]
	sub	bx,__psp		; Calculate the correct offset for
	mov	cl,4			; the interrupt handler if the segment
	shl	bx,cl			; must be that of our PSP
	add	dx,bx
	mov	ds,__psp
	mov	ah,MS_S_SETINT
	int	DOS_INT
	pop	ds
	ret

page
;
;	This routine will restore the Int 22 terminate address copy in
;	the PSP in preperation for an EXIT (DeskView bug).
;
	Public	_restore_term_addr
_restore_term_addr:
	push	es
	mov	ax,__psp			; Restore the PSP we altered
	mov	es,ax				; if the current process
	cmp	ax,PSP_PARENT			; is the ROOT DOS process. ie
	 jne	restore_ta10			; PSP_PARENT == PSP

	push	si
	push	di
	lea	si,psp_save_area
	lea	di,PSP_TERM_IP
	mov	cx,6
	rep	movsw
	pop	di
	pop	si

restore_ta10:
	pop	es
	ret

page
;
;	This routine will restore the Novell error mode - BAP
;	
	Public	_restore_error_mode
_restore_error_mode:
	mov	dl,net_error_mode
	mov	ah,0ddh			; set error mode to the value
	int	21h			; it was when COMMAND was
	ret				; executed

page
;
;	MASTER_ENV will create a Master Environment of 04[bp] bytes unless
;	the environment is already larger in which case the environment
;	is increased by 128 bytes.
;
	Public	_master_env
_master_env:
	push	bp
	mov	bp,sp
	push	si
	push	di
	push	es
ifdef DOSPLUS
;	mov	ax,(MS_M_STRATEGY*256)+1
;	mov	bx,1			; set memory strategy to best fit
;	int	DOS_INT
endif
	mov	bx,04[bp]		; Save the Specified Size
	add	bx,15			; and force it to be an integer 
	and	bx,not 15		; multiple of 16
	mov	es,__psp		; Get the PSP Segment Address
	
	mov	cx,PSP_ENVIRON		; Get the environment segemnt
	jcxz	master_env10		; Have we been loaded by DesQview ?

	mov	es,cx			; Scan through the environment and
	xor	di,di			; determine the Environment size and
	xor	ax,ax			; the Load file name
	mov	cx,7FFFh

master_env05:
	repne	scasb			; Scan for a ZERO byte
	jcxz	master_env10		; Abort if maximum size exceeded
	inc	di			; Check if the next character was a 0
	cmp	al,es:-1[di]		; if YES then this is the end of the
	jnz	master_env05		; environment
	mov	cx,di			; Calculate the environment Size
;
;	CX contains the current environment size in bytes. 0 for a DesQview
;	system or an empty environment.
;
master_env10:	
	cmp	bx,cx			; Is the current environment larger
	jae	master_env15		; No allocate requested value
	mov	bx,128
	add	bx,cx

master_env15:
	push	cx			; Save current Environment Size
	mov	cl,4			; convert request to paragraphs
	shr	bx,cl
	mov	ah,MS_M_ALLOC		; Allocate Memory
	int	DOS_INT
	pop	cx
	jc	master_env30		; Abort Memory Allocation Failed

	mov	es,__psp		; Copy the Environemt
	xchg	ax,PSP_ENVIRON		; Update the Environment Pointer
	push	ds
	mov	ds,ax			; DS -> Initial Environment
	mov	es,PSP_ENVIRON		; ES -> Master Environment
	mov	si,0
	mov	di,si
	jcxz	master_env20		; If this was a Desqview exec then
	rep	movsb			; skip the environment copy and
					; just initialize to 0000

if TRUE					; Invalidate the contents of the
	not	ds:word ptr 00h		; current environment so Novell
else					; Netware finds the correct Master
	push	es			; environment under Concurrent DOS.
	mov	es,ax			;
	mov	ah,MS_M_FREE		; A possible alternative is to free
	int	DOS_INT			; the old environment but this would
	pop	es			; change the memory allocations for
endif					; sub-sequent loads.

master_env20:
	pop	ds
	xor	ax,ax
	stosw

master_env30:
ifdef DOSPLUS
;	mov	ax,(MS_M_STRATEGY*256)+1
;	xor	bx,bx			; set memory strategy to first fit
;	int	DOS_INT
endif
	pop	es
	pop	di
	pop	si
	pop	bp
	ret

endif
	page
ifndef DOSPLUS
;
;	UWORD FAR *sysdat(WORD *);
;
	Public _sysdat
_sysdat:
	push	bp
	mov	bp,sp
	push	es
	BDOS	S_SYSDAT		; Get the SYSDAT segment address
	mov	ax,word ptr 04[bp]	; and the SYSDAT byte offset
	mov	bx,es			; and return a FAR pointer to the
	mov	dx,bx			; data required. MSC uses DX:AX
	pop	es			; but cater for other compilers 
	pop	bp			; just in case.
	ret
endif

ifdef DOSPLUS
;
;	BOOLEAN CDECL int10_cls();
;
;	int10_cls will return TRUE if it issued an INT10 function to
;	clear the screen.
;
	Public	_int10_cls
_int10_cls:
	push	bp
	push	si
	push	di
	push	es
	mov	ax,(MS_X_IOCTL*256)+0	; Get the device attributes for
	mov	bx,STDOUT		; STDOUT
	int	DOS_INT
	mov	ax,0000			; Assume that the test fails
	 jc	int10_exit		; and return FALSE to the caller
	and	dl,092h			; Check that STDOUT is the Console
	cmp	dl,092h			; Out DEVICE and it supports INT29
	 jnz	int10_exit		; Fast output

	;;mov	es,ax			; Now check that the INT29 routine
	;;mov	bx,es:word ptr (29h*4)+2; is below the INT20 service routine
	;;cmp	bx,es:word ptr (20h*4)+2; ie that this is a BIOS device driver
	;; jae	int10_exit		; No way Jose
					; So what if it isn't?

	mov	ax,1A00h		; ANSI.SYS installation check
	int	2Fh			; AL = FF on return if ANSI installed
	cbw				; AX = FFFF if ANSI present
	inc	ax			; AX = 0 if ANSI present
	 jz	int10_exit


;	get number of lines

	call	cginfo			; get screen resolution
	push	dx			; screen lines

	mov	ah, 0fh			; get mode
	int	10h
	and	al,7fh
	mov	ah, 0			; set mode, clear screen (al bit 7 clear)
	int	10h
	
	call	cginfo			; has resolution changed?
	pop	ax			; restore screen lines

	cmp	al, dl			; has # of screen lines changed?
	 je	int10_done		; skip if still the same
	
	mov	ax, 1112h		; character generator
	mov	bl, 0			; set 8x8 double dot font
	int	10h
	
	mov	ax, 1103h		; set block specifier
	mov	bl, 0
	int	10h

int10_done:	
	mov	ax,1			; All done

int10_exit:
	pop	es
	pop	di
	pop	si
	pop	bp
	ret

cginfo:
	mov	dl, 24			; assume default # for CGA/MDA
	mov	ax, 1130h		; character generator info
	mov	bh, 0
	int	10h
	ret				; dl = nlines - 1

endif

ifdef CDOSTMP

xios:
	push	ds			; Save the entry segment registers
	push	es			; then call the XIOS correctly with 
					; DS pointing to SYSDAT and ES to the
	mov	es,uda			; current process's UDA
	mov	ds,sysdat
	call	XIOS_ENTRY
	pop	es
	pop	ds
	ret

endif

ifndef DOSPLUS
;
;	_vc_data(&vc_base, &vc_num, &pc_num)
;
;	VC_DATA returns the first Virtual Console attached to the current
;	physical console, the number of virtual consoles attached and the
;	physical console.
;
	Public	_vc_data
_vc_data:
	push	bp
	mov	bp,sp
	push	es
	push	si
	push	di
	mov	si,04[bp]		; SI == &VC_BASE
	mov	word ptr [si],0000	; *vc_base = 0

	les	bx,_pd			; Get our process descriptor address
	mov	ah,0
	mov	al,NVCNS
	mov	cx,ax
	mov	al,es:P_CNS[bx]		; get the current VC No.
	mov	bx,CCBLIST		; XIOS CCB$LIST
	mov	di,ax			; Calculate the correct entry in
	shl	di,1			; the CCB$LIST which points to our
	mov	di,es:word ptr [di+bx]	; CCB and then extract the correct
	mov	al,CCB_PCNS[di]		; Physical Console Number.
	mov	di,08[bp]		; Get the address of the PC_NUM
	mov	word ptr [di],ax	; and save the Physical Console Number
	mov	di,0000			; From CCB 0

vc_d10:
	push	di
	mov	di,es:word ptr [di+bx]	; Get the CCB ad…ress
	cmp	al,CCB_PCNS[di]		; Is this theY$ame physical console
	pop	di			; Restore the original DI
	jz	vc_d30			; Yes then save info
vc_d20:
	inc	word ptr [si]		; *VC_BASE++
	inc	di			; Point to the next entry in
	inc	di			; the CCB$LIST and try again
	loop	vc_d10
	jmp	vc_exit

vc_d30:
	mov	si,06[bp]		; VC_NUM
	mov	word ptr [si],0000	; *VC_NUM = 0
vc_d40:
	push	di
	mov	di,es:word ptr [di+bx]	; Get the CCB address
	cmp	al,CCB_PCNS[di]		; Is this the same physical console
	pop	di			; Yes then increment the count
	jnz	vc_exit			; and continue.
	inc	word ptr [si]		; *VC_NUM++
	inc	di			; Point to the next entry in
	inc	di			; the CCB$LIST and try again
	loop	vc_d40
	
vc_exit:
	pop	di
	pop	si
	pop	es
	pop	bp
	ret
endif

_TEXT	ENDS
	page
ifdef CDOSTMP

RSF_DYNAMIC	equ	0001h			; create at boot time
RSF_NONBANK	equ	0002h			; allocate non-banked
RSF_SPECIAL	equ	0004h			; requires separate code
RSF_ENVIRON	equ	0008h			; requires large environment

PD_SEG	SEGMENT
;
;	This is the standard process descriptor for a TMP. During 
;	the Concurrent P_CREATE function the contents of this process 
;	descriptor are copied to a full size descriptor inside SYSDAT.
;
		dw	0,0			; link fields
		db	PS_RUN			; status
		db	200			; priority
		dw	PF_SYS+PF_KEEP+PF_SPECIAL; flags
		db	'Tmp     '		; Process Name
		dw	40h/10h			; uda seg
		db	0,0			; disk,user
                db      0,0             	; ldisk,luser
		dw	0FFFFh 			; mem (Shared Code)
		dw	0,0			; dvract,wait
                db      0,0             	; org,net
                dw      0               	; parent
cns             db      0,0             	; cns,abort
                db      0,0             	; cin,cout
                db      0,0             	; lst,sf3
                db      0,0             	; sf4,sf5
		dw	0,0			; reserved
                dw      0,0             	; pret,scratch
PD_SEG	ENDS

UDA_SEG	SEGMENT
uda_size	dw	ULEN,80h,0,0		;0-7
		dw	0,0,0,0			;8-fh
		dw	0,0,0,0			;10-17
		dw	0,0,0,0			;18-1f
		dw	0,0,0,0			;20-27
		dw	0,0,0,0			;28-2f
		dw	0,0			;30-33
uda_SP		dw	dataOFFSET uda_stack,0	;34-37
		dw	0,0,0,0			;38-3f
		dw	0,0,0,0			;40-47
		dw	0,0,0,0			;48-4f
uda_CS		dw	0			;50-51
uda_DS		dw	0			;52-53
uda_ES		dw	0			;54-55	
uda_SS		dw	0			;56-57
		dw	0,0,0,0			;58-5f
insys		db	1,0			;60-61
		dw	0,0,0			;62-67
		db	(ULEN-6Eh)dup(0CCH)	; Initialise System Stack
uda_stack	dw	codeOFFSET RSP_start	; Initial Offset	
		dw	?			; Initial Segment (Unknown)
		dw	?			; Initial Flags (Unknown)
UDA_SEG	ENDS

endif

ifdef DOSPLUS
_TEXT	SEGMENT
	public	_show_help
	public	_put_resident_high
	public	_get_config_env	
	public	_get_original_envsize		; BAP added this

_show_help	PROC NEAR
;
;	VOID	show_help(index)
;	WORD	index
;
	;out	0fdh,al

	push	bp
	mov	bp,sp
	sub	sp,4			; require 2 WORD local variables
	
	mov	ah,MS_M_ALLOC		; allocate memory for the help text
	mov	bx,help_length		; bx = no. paragraphs
	mov	cl,4
	shr	bx,cl
	int	DOS_INT			; do it
	jnc	show_help_05
	jmp	show_help_err0		; exit on error
show_help_05:
	mov	-2[bp],ax		; save segment of allocated memory

	push	ds
	mov	ds,[low_seg]
	mov	dx,dataOFFSET reload_file
	mov	ax,(MS_X_OPEN*256)+0	; Open the COMMAND.COM file
	int	DOS_INT			; do it
	pop	ds
	jc	show_help_err1		; exit on error
	mov	bx,ax			; bx = file handle
	mov	-4[bp],ax		; save handle for later
		
	xor	cx,cx
	xor	dx,dx
	cmp	exe_file,TRUE		; if command.com is an EXE file take
	jne	show_help_10		; account of the EXE header
	add	dx,200h
show_help_10:
	add	dx,total_length
	add	dx,cgroup_length
	add	dx,cend_length		; dx = file offset of help text
	mov	ax,(MS_X_LSEEK*256)+0	; seek to the right location
	int	DOS_INT			; do it
	jc	show_help_err2		; exit on error
	
	push	ds
	mov	ds,-2[bp]		; ds = help text segment
	xor	dx,dx			; dx = 0; 
	mov	ah,MS_X_READ		; read from COMMAND.COM file
	mov	cx,help_length		; cx = no. bytes required
	int	DOS_INT			; do it
	pop	ds
	jc	show_help_err2		; exit on error
	cmp	ax,0			; zero bytes read means there's no
	je	show_help_err2		; help seg tagged to file.

ifdef DLS
	call	_get_msg_lang		; get a far pointer to msg_language var
	mov	es,dx			; dx:ax = far pointer on return
	mov	bx,ax			;
	mov	bx,es:[bx]		; bx = msg_language
	shl	bx,1			; multiply bx by 2
else
	xor	bx,bx			; bx = 0
endif
	mov	es,-2[bp]		; ds = help text segment
	mov	bx,es:[bx]		; bx -> help message offset table
	mov	ax,4[bp]		; ax = message index
	shl	ax,1			; multiply by 2
	add	bx,ax			; bx -> offset of required message
	mov	bx,es:[bx]		; bx -> message

	call	write_string		; display the message

show_help_err2:
	mov	ah,MS_X_CLOSE		; close the file
	mov	bx,-4[bp]		; bx = file handle
	int	DOS_INT			; do it

show_help_err1:
	mov	ah,MS_M_FREE		; free the memory
	push	es
	mov	es,-2[bp]		; es = segment to free
	int	DOS_INT			; do it
	pop	es
	
show_help_err0:
	add	sp,4
	pop	bp
	ret


write_string:
	;extrn	_str:byte
	
	; es -> help segment
	; bx -> message in help segment

write_string_00:
	;mov	di,dataOFFSET _str	; di -> local buffer
	mov	di,[heap_top]
write_string_05:
	mov	al,es:[bx]		; get a character
	cmp	al,0ah			; check for NEWLINE...
	jnz	write_string_10		; ...jump if its not
		
	mov	al,0dh			; replace LF with CR LF
	mov	[di],al			; 
	inc	di			; 
	mov	al,0ah			;
	mov	[di],al			; 
	inc	di			;
	mov	al,0			;
	mov	[di],al			; terminate string
	call	flush_buff		; display it
	inc	bx			;
	jmp	write_string_00		; start again

write_string_10:
	cmp	al,0			; check for NULL...
	jnz	write_string_20		; ...jump if its not
	
	mov	[di],al			; store char
	call	flush_buff		; display string
	jmp	write_string_90		; exit

write_string_20:
	cmp	al,'%'			; messages have doubled %'s because
	jnz	write_string_30		; were intended for printf originally.
	inc	bx			; => skip next character.
write_string_30:
	mov	[di],al			; store char
	inc	di			;			
	inc	bx			;
	jmp	write_string_05		; loop

write_string_90:
	ret

flush_buff:
	extrn	strlen_:near
	extrn	c_write_:near

	;mov	ax,dataOFFSET _str	; watcom C requites first param in AX
	mov	ax,[heap_top]
	call	strlen_			; get length of string
	mov	dx,ax			; watcom C requires second param in DX
	;mov	ax,dataOFFSET _str	; watcom C requires first param in AX
	mov	ax,[heap_top]
	call	c_write_		; display the string
	ret

_show_help	ENDP



try_high_memory		PROC NEAR
; This function attempts to allocate some high memory at FFFF:E0 by modifying
; the HIMEM FREE CHAIN maintained by the BDOS.
; on entry: bx = no. paras required (preserved)
; on exit:  Carry Set = unsuccessful
;           otherwise
;           ax = FFFF = segment of allocated memory
;           HIMEM Free Chain is modified.
  

	push	ds
	push	es
	push	bx
	
	mov	cl,4		; multiplying bx by 4...
	shl	bx,cl		; ...gives memory required in bytes
	mov	dx,bx		; put it in dx because bx is used for
				; somthing else


	add	dx,HISEG_OFF

	mov	ax,4458h	; get private data area
	int	DOS_INT		; do it
	mov	si,es:10h[bx]	; ax = start of HIMEM Free Chain
	test	si,si		; zero means there's no chain so...
	je	thm_unsuccessful; ...return unsuccessful

	mov	ax,0FFFFh	;
	mov	ds,ax		; ds:si -> first free himem area
	
	cmp	si,HISEG_OFF	; check if area starts at or below HISEG_OFF
	ja	thm_unsuccessful; ...return unsuccessful	
	mov	cx,2[si]	; CX = length of free area
	cmp	cx,dx		; check if length >= that required
	jb	thm_unsuccessful; ...return unsuccessful	

	mov	ax,[si]		; assume we will use the entire block
	mov	es:10h[bx],ax	;  so unlink it from the chain

	mov	di,HISEG_OFF	; generate HIMEM REGISTRATION link and
	mov	ax,di		;  update root in private data area
	xchg	ax,es:14h[bx]
	mov	[di],ax
	mov	2[di],dx	; remember how much we have used

; JBM commented this line out, I put it back in. (BAP)
	sub	2[di],di
; see above comment (JBM)

	sub	cx,dx		; subtract amount used from actual size
	cmp	cx,256		; if less than 256 bytes are left
	jb	thm_success	;  then just forget about them

	mov	si,di		; there is enough to be worth recyling
	add	si,dx		; SI -> free block following allocation
	mov	2[si],cx	; it's this long
	mov	ax,si
	xchg	ax,es:10h[bx]	; put at head of HMA free chain
	mov	[si],ax
thm_success:
	mov	ax,0ffffh	; return success
	clc			;
	jmp	thm_exit	;
	
thm_unsuccessful:
	stc			; return failure
thm_exit:
	pop	bx
	pop	es
	pop	ds
	ret

try_high_memory		ENDP


_put_resident_high	PROC NEAR
;
;	VOID	put_resident_high(param)
;	WORD	param;
;	
;	param = 0	Try HIGH memory, then UPPER memory
;	param = 1	Only try HIGH memory
;	param = 2	Only try UPPER memory
;

PRH_PARAM	equ word ptr 4[bp]

	push	bp
	mov	bp,sp
	push	si
	push	di

	
	mov	ax,(MS_M_STRATEGY*256)+2
	int	DOS_INT			; get existing HMA link
	mov	ah,0
	push	ax			; save it
	mov	bx,total_length		; get size of resident code/data
	sub	bx,(dataOFFSET hi_seg_start)-15
	mov	cl,4			; subtract size of low memory stub
	shr	bx,cl			; bx = block size in paras.

	cmp	PRH_PARAM,2		; skip the next bit if we're only 
	je	prh_upper		; looking at UPPER memory

	call	try_high_memory		; first try to allocate high memory
					; ie seg FFFF
	jnc	prh_success		; jump if successful

	cmp	PRH_PARAM,0		; if we're only looking at HIGH memory
	jne	prh_exit		; then we exit now

prh_upper:
	push	bx			; save length
	mov	ax,(MS_M_STRATEGY*256)+1
	mov	bx,41h			; set memory strategy to best fit
	int	DOS_INT			;  upper only
	mov	ax,(MS_M_STRATEGY*256)+3
	mov	bx,1			; try to link in upper memory
	int	DOS_INT
	pop	bx			; recover length
	 jc	prh_exit		; no upper memory, stop now

	mov	ah,MS_M_ALLOC		; allocate some memory
	int	DOS_INT			; do it
	 jc	prh_exit		; no upper memory, stop now

	sub	ax,HISEG_OFF/16		; bias segment appropriately

prh_success:
;	AX = segment to relocate to
;
	mov	es,ax			; es = new block
	
	mov	si,dataOFFSET hi_seg_start ; start at R_TEXT segment
	mov	di,si

	mov	cx,total_length		; get size of resident code/data
	sub	cx,si			; subtract size of low memory stub

	mov	[reloc_seg],ax		; These values in the low memory
	mov	[reloc_off],di		; stub are used by TaskMAX to find
	mov	[reloc_size],cx		; the relocated code/data


	mov	[data_seg],ax		; update data_seg variable
	mov	[exec_seg],ax		; and some others...
	mov	[readline_seg],ax
	mov	[func4b_seg],ax
	mov	[int2E_seg],ax
	mov	ds:word ptr [control_break_entry+3],ax
	mov	ds:word ptr [crit_error_entry+3],ax

	add	si,4			; take account of HIMEM
	add	di,4			; REGISTRATION link
	sub	cx,4	

	rep	movsb			; move'em
	
	mov	bx,ds
	sub	bx,[__psp]		; ax = difference between psp and data
	add	bx,HISEG_OFF/16		; add size of bit we leave behind

	mov	es,[__psp]		; es -> old location	

	mov	ds,ax			; this is the new data seg
	mov	ss,ax			; and also the new stack seg

	mov	ah,MS_M_SETBLOCK	; modify old segment size 
	int	DOS_INT			; do it

prh_exit:
	pop	bx			; recover upper memory link
	mov	ax,(MS_M_STRATEGY*256)+3
	int	DOS_INT			; restore to original state

	mov	ax,(MS_M_STRATEGY*256)+1
	xor	bx,bx			; set memory strategy to first fit
	int	DOS_INT

	
	pop	di
	pop	si
	pop	bp

	ret

_put_resident_high	ENDP


_get_config_env		PROC NEAR

	; BYTE	FAR *get_config_env();
	; This function returns a pointer to the start of the config.sys
	; environment.
	
	mov	ax,4458h	; get pointer to private data
	int	DOS_INT		; do it
	mov	ax,0		; es:bx -> private data
	 jc	get_cfg_env10
	xchg	ax,es:18[bx]	; ax = segment of config environment
get_cfg_env10:
	mov	bx,ax		; return FARNULL (0000:0000)
	mov	dx,ax	
	xor	ax,ax
	ret

_get_config_env		ENDP


; BAP - This routine finds the orignal COMMAND.COM's environment size.
; This may not be the best way of doing it .. !
; It finds the PSP of the original COMMAND.COM by checking the PSP seg
; against the parent PSP seg. If they are not the same, it repeats for
; the parent. When they are the same, it has found the original COMMAND
; and finds the environment size.

_get_original_envsize	PROC	NEAR
	push	bp
	mov	bp,sp
	push	si
	push	di
	push	es
	push	cx
	mov	ah,51h
	int	DOS_INT			; get current PSP seg
try_next:
	mov	es,bx
	mov	cx,bx			; move into CX
	mov	bx,0
	mov	ax,es:16h[bx]		; get parent PSP seg in ax
	cmp	ax,cx			; are they the same ?
	je	got_org_psp		; yes - found COMMAND.COM PSP
	mov	bx,ax			; else make this current seg and
	jmp	try_next		; try again

got_org_psp:
	mov	es,ax			; ES = COMMAND.COM PSP seg
	mov	bx,0
	mov	ax,es:2ch[bx]		; get env seg in ax
	cmp	ax,0			; seg = 0000 ?
	je	bomb_out		; yes - forget it
	dec	ax			; AX:0000 points to memory descriptor
	mov	es,ax
	mov	ax,es:3[bx]		; find length of seg ( in paras)
	mov	cl,4
	shl	ax,cl			; convert to bytes
bomb_out:
	mov	bx,ax
	mov	dx,ax
	pop	cx
	pop	es
	pop	di
	pop	si
	pop	bp
	ret
_get_original_envsize	ENDP


_TEXT	ENDS
endif

ifndef CDOSTMP

R_TEXT	SEGMENT
	extrn	_out_pipe:byte
	extrn	_kbdbuf:byte
R_TEXT		ENDS

_TEXT	SEGMENT

	public	_get_reload_file
_get_reload_file:

; copy reload_file to heap

	push	ds
	push	es
	push	si
	push	di

	;mov	ax,0e40h
	;int	10h

	mov	ax,ds
	mov	es,ax
	mov	ds,[low_seg]
	mov	di,[heap_top]
	mov	si,offset reload_file
	cld
grf_loop:
	lodsb
	stosb
	cmp	al,0
	jnz	grf_loop	

	pop	di
	pop	si
	pop	es
	pop	ds
	ret
	
	public	_set_reload_file
_set_reload_file:

; copy string on heap to reload file

	push	es
	push	si
	push	di

	;mov	ax,0e40h
	;int	10h

	mov	es,[low_seg]
	mov	si,[heap_top]
	mov	di,offset reload_file
	cld
srf_loop:
	lodsb
	stosb	

	cmp	al,20h			; BAP from here
	jne	srf_brian		; if AL = 20h, poke a 00
	xor	al,al			; in instead, to terminate
	dec	di			; the file. COMSPEC can then have
	stosb				; switches, but reload_file is
srf_brian:				; just the file name.

	cmp	al,0
	jnz	srf_loop

	pop	di
	pop	si
	pop	es
	ret

	public	_get_out_pipe
_get_out_pipe:

; copy out_pipe filename from low_seg to data seg

	push	ds
	push	es
	push	si
	push	di

	;mov	ax,0e40h
	;int	10h

	mov	ax,ds
	mov	es,ax
	mov	di,offset DGROUP:_out_pipe
	
	mov	ds,[low_seg]
	mov	si,offset out_pipe
	
	mov	cx,8
	cld
	rep	movsb	

	pop	di
	pop	si
	pop	es
	pop	ds
	ret

	public	_docmd_int2f
;
;	BOOLEAN	docmd_int2f(BYTE *cmdline, BYTE *cmd, UWORD count);
;
;	cmdline	db	max, actual, 'COMMAND LINE', CR
;	cmd	db	length, 'COMMAND', CR
;	count	db	remaining length of tail, FF/00 internal/external flag
;
;	

_docmd_int2f:

	push	bp
	mov	bp,sp
	push	bx
	push	si

	mov	bx,4[bp]			; bx -> original command line
	mov	si,6[bp]			; si -> upper cased command
	mov	cx,8[bp]
	mov	dx,0ffffh
	
	mov	ax,0AE00h
	int	2fh	
	
	test	al,al
	 jz	docmd_int2f_exit

	mov	bx,4[bp]
	mov	si,6[bp]
	mov	cx,8[bp]
	mov	dx,0ffffh
	
	mov	ax,0AE01h
	int	2fh
	mov	al,1
docmd_int2f_exit:
	cbw				; return true if handled
	pop	si
	pop	bx
	pop	bp
	mov	bx,ax
	mov	dx,ax
	ret

_TEXT	ENDS

endif

	end				; start address
