;[]------------------------------------------------------------[]
;|	C0.ASM -- Start Up Code for DOS 			|
;[]------------------------------------------------------------[]

;
;	C/C++ Run Time Library - Version 5.0
;
;	Copyright (c) 1987, 1992 by Borland International
;	All Rights Reserved.
;

		locals

		__C0__ = 1
INCLUDE 	RULES.ASI

;	Segment and Group declarations

_TEXT		SEGMENT BYTE PUBLIC 'CODE'
		ENDS
_FARDATA	SEGMENT PARA PUBLIC 'FAR_DATA'
		ENDS
_FARBSS 	SEGMENT PARA PUBLIC 'FAR_BSS'
		ENDS
_DATA		SEGMENT PARA PUBLIC 'DATA'
		ENDS
_INIT_		SEGMENT WORD PUBLIC 'INITDATA'
InitStart	label byte
		ENDS
_INITEND_	SEGMENT BYTE PUBLIC 'INITDATA'
InitEnd 	label byte
		ENDS
_EXIT_		SEGMENT WORD PUBLIC 'EXITDATA'
ExitStart	label byte
		ENDS
_EXITEND_	SEGMENT BYTE PUBLIC 'EXITDATA'
ExitEnd 	label byte
		ENDS
_CVTSEG 	SEGMENT WORD PUBLIC 'DATA'
		ENDS
_SCNSEG 	SEGMENT WORD PUBLIC 'DATA'
		ENDS
  _BSS		SEGMENT WORD PUBLIC 'BSS'
		ENDS
  _BSSEND	SEGMENT BYTE PUBLIC 'BSSEND'
		ENDS
DGROUP group _DATA
	ASSUME	CS:_TEXT, DS:DGROUP

;	External References

extrn	    _exit:near
extrn	    __exit:near
extrn	    __setupio:near	    ;required!
extrn	    __stklen:word
extrn	    __heaplen:word

	SUBTTL	Start Up Code
	PAGE
;/*							*/
;/*-----------------------------------------------------*/
;/*							*/
;/*	Start Up Code					*/
;/*	-------------					*/
;/*							*/
;/*-----------------------------------------------------*/
;/*							*/
PSPHigh 	equ	00002h
PSPEnv		equ	0002ch
PSPCmd		equ	00080h

		public	__AHINCR
		public	__AHSHIFT
__AHINCR	equ	1000h
__AHSHIFT	equ	12

MINSTACK	equ	128	; minimal stack size in words
;
;	At the start, DS and ES both point to the segment prefix.
;	SS points to the stack segment except in TINY model where
;	SS is equal to CS
;
_TEXT		SEGMENT
extrn		main:near
STARTX		PROC	NEAR
;	Save general information, such as :
;		DGROUP segment address
;		DOS version number
;		Program Segment Prefix address
;		Environment address
;		Top of far heap

		mov	dx, DGROUP	; DX = GROUP Segment address
		mov	cs:DGROUP@@, dx ;  __BOSS__
		mov	ah, 30h
		int	21h		; get DOS version number
		mov	bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
		mov	bx, ds:[PSPEnv] ; BX = Environment Segment address
		mov	ds, dx
		mov	_version@, ax	; Keep major and minor version number
		mov	_psp@, es	; Keep Program Segment Prefix address
		mov	_envseg@, bx	; Keep Environment Segment address
		mov	word ptr _heaptop@ + 2, bp
;
;	Save several vectors and install default divide by zero handler.
;
		call	SaveVectors


;	Count the number of environment variables and compute the size.
;	Each variable is ended by a 0 and a zero-length variable stops
;	the environment. The environment can NOT be greater than 32k.

		mov	ax, _envseg@
		mov	es, ax
		xor	ax, ax
		mov	bx, ax
		mov	di, ax

		mov	cx, 07FFFh	; Environment cannot be > 32 Kbytes
		cld
@@EnvLoop:
		repnz	scasb
		jcxz	InitFailed	; Bad environment !!!

		inc	bx		; BX = Nb environment variables
		cmp	es:[di], al
		jne	@@EnvLoop	; Next variable ...
		or	ch, 10000000b
		neg	cx
		mov	_envLng@, cx	; Save Environment size
		mov	cx, dPtrSize / 2
		shl	bx, cl
		add	bx, dPtrSize * 4
		and	bx, not ((dPtrSize * 4) - 1)
		mov	_envSize@, bx	; Save Environment Variables Nb.


;	Determine the amount of memory that we need to keep

		mov	dx, ds
		sub	bp, dx		; BP = remaining size in paragraphs
;		 mov	 di, __stklen	 ; DI = Requested stack size
;
; Make sure that the requested stack size is at least MINSTACK words.
;
		cmp	di, 2*MINSTACK	; requested stack big enough ?
		jae	AskedStackOK
		mov	di, 2*MINSTACK	; no --> use minimal value
		mov	   __stklen, di ; override requested stack size

AskedStackOK	label	near
		add	di, offset DGROUP: edata@
		jb	InitFailed	; DATA segment can NOT be > 64 Kbytes
		add	di, __heaplen
		jb	InitFailed	; DATA segment can NOT be > 64 Kbytes
		mov	cl, 4
		shr	di, cl		; $$$ Do not destroy CL $$$
		inc	di		; DI = DS size in paragraphs
		cmp	bp, di
		jb	InitFailed	; Not enough memory
		cmp	__stklen, 0
		je	ExpandDS	; Expand DS up to 64 Kb
		cmp	__heaplen, 0
		jne	ExcessOfMemory	; Much more available than needed
ExpandDS	label	near
		mov	di, 1000h
		cmp	bp, di
		ja	ExcessOfMemory	; Enough to run the program
		mov	di, bp
		jmp	short ExcessOfMemory  ; Enough to run the program

;	All initialization errors arrive here

InitFailed	label	near
		jmp	near ptr _abort

;	Return to DOS the amount of memory in excess
;	Set far heap base and pointer

ExcessOfMemory	label	near
		mov	bx, di
		add	bx, dx
		mov	word ptr _heapbase@ + 2, bx
		mov	word ptr _brklvl@ + 2, bx
		mov	ax, _psp@
		sub	bx, ax		; BX = Number of paragraphs to keep
		mov	es, ax		; ES = Program Segment Prefix address
		mov	ah, 04Ah
		push	di		; preserve DI
		int	021h		; this call clobbers SI,DI,BP !!!!!!
		pop	di		; restore  DI

		shl	di, cl		; $$$ CX is still equal to 4 $$$

		mov	ss, dx		; Set the program stack
		mov	sp, di

;	Reset uninitialized data area

		xor	ax, ax
		mov	es, cs:DGROUP@@
		mov	di, offset DGROUP: bdata@
		mov	cx, offset DGROUP: edata@
		sub	cx, di
		cld
		rep	stosb

;   If default number of file handles have changed then tell DOS
		cmp	__nfile, 20
		jbe	@@NoChange
		cmp	_osmajor@, 3   ; Check for >= DOS 3.3
		jb	@@NoChange
		ja	@@DoChange
		cmp	_osminor@, 1Eh
		jb	@@NoChange
@@DoChange:
		mov	ax, 5801h      ; Set last fit allocation
		mov	bx, 2
		int	21h
		jc	@@BadInit

		mov	ah, 67h        ; Expand handle table
		mov	bx, __nfile
		int	21h
		jc	@@BadInit

		mov	ah, 48h        ; Allocate 16 bytes to find new
		mov	bx, 1	       ;   top of memory address
		int	21h
		jc	@@BadInit
		inc	ax	       ; Adjust address to point after block
		mov	word ptr _heaptop@ + 2, ax

		dec	ax	       ; Change back and release block
		mov	es, ax
		mov	ah, 49h
		int	21h
		jc	@@BadInit

		mov	ax, 5801h      ; Set first fit allocation
		mov	bx, 0
		int	21h
		jnc	@@NoChange

@@BadInit:	jmp near ptr _abort

@@NoChange:

;	Prepare main arguments

		mov	ah, 0
		int	1ah			; get current BIOS time in ticks
		mov	word ptr _StartTime@,dx ; save it for clock() fn
		mov	word ptr _StartTime@+2,cx
		or	al,al			; was midnight flag set?
		jz	@@NotMidnight
		mov	ax,40h			; set BIOS midnight flag
		mov	es,ax			;  at 40:70
		mov	bx,70h
		mov	byte ptr es:[bx],1

@@NotMidnight:
		xor	bp,bp			; set BP to 0 for overlay mgr

		mov	es, cs:DGROUP@@
		mov	si,offset DGROUP:InitStart	;si = start of table
		mov	di,offset DGROUP:InitEnd	;di = end of table

;	ExitCode = main(argc,argv,envp);
		mov	ax,ss
		mov	OriginalSS,ax
		mov	ax,sp
		mov	OriginalSP,ax
		mov	ax,bp
		mov	OriginalBP,ax
IF	LDATA
		push	word ptr __C0environ+2
		push	word ptr __C0environ
		push	word ptr __C0argv+2
		push	word ptr __C0argv
ELSE
		push	word ptr __C0environ
		push	word ptr __C0argv
ENDIF
		push	__C0argc
		call	Near Ptr main
;		 add	 sp,6
;	Flush and close streams and files

		push	ax
		call	_exit

;---------------------------------------------------------------------------
;	_checknull()	check for null pointer zapping copyright message
;	_terminate(int) exit program with error code
;
;	These functions are called by exit(), _exit(), _cexit(),
;	and _c_exit().
;---------------------------------------------------------------------------

;	Check for null pointers before exit

__checknull	PROC	DIST
		PUBLIC	__checknull

		push	si
		push	di
		mov	es, cs:DGROUP@@
		xor	ax, ax
		mov	si, ax
		mov	cx, lgth_CopyRight
ComputeChecksum label	near
		add	al, es:[si]
		adc	ah, 0
		inc	si
		loop	ComputeChecksum
		sub	ax, CheckSum
		jz	@@SumOK
		mov	cx, lgth_NullCheck
		mov	dx, offset DGROUP: NullCheck
		call	ErrorDisplay
@@SumOK:	pop	di
		pop	si
		ret
__checknull	ENDP

;	Exit to DOS

__terminate	PROC	DIST
		PUBLIC	__terminate
		mov	bx,OriginalSS
		mov	cx,OriginalSP
		mov	ss,bx
		mov	sp,cx
		mov	bp,OriginalBP
		mov	ah,4Ch
		mov	al,_ResultCode@
		int	21h			; Exit to DOS
__terminate	ENDP

STARTX		ENDP

	SUBTTL	Vector save/restore & default Zero divide routines
	PAGE
;[]------------------------------------------------------------[]
;|								|
;| Interrupt Save/Restore routines and default divide by zero	|
;| handler.							|
;|								|
;[]------------------------------------------------------------[]

ZeroDivision	PROC	FAR
		mov	cx, lgth_ZeroDivMSG
		mov	dx, offset DGROUP: ZeroDivMSG
		jmp	MsgExit3
ZeroDivision	ENDP

;--------------------------------------------------------------------------
;	savevectors()
;
;	Save vectors for 0, 4, 5 & 6 interrupts.  This is for extended
;	signal()/raise() support as the signal functions can steal these
;	vectors during runtime.
;--------------------------------------------------------------------------
SaveVectors	PROC	NEAR
		ret
		push	ds
; Save INT 0
		mov	ax, 3500h
		int	021h
		mov	word ptr _Int0Vector@, bx
		mov	word ptr _Int0Vector@+2, es
; Save INT 4
		mov	ax, 3504h
		int	021h
		mov	word ptr _Int4Vector@, bx
		mov	word ptr _Int4Vector@+2, es
; Save INT 5
		mov	ax, 3505h
		int	021h
		mov	word ptr _Int5Vector@, bx
		mov	word ptr _Int5Vector@+2, es
; Save INT 6
		mov	ax, 3506h
		int	021h
		mov	word ptr _Int6Vector@, bx
		mov	word ptr _Int6Vector@+2, es
;
;	Install default divide by zero handler.
;
		mov	ax, 2500h
		mov	dx, cs
		mov	ds, dx
		mov	dx, offset ZeroDivision
		int	21h

		pop	ds
		ret
SaveVectors	ENDP

;--------------------------------------------------------------------------
;	_restorezero() puts back all the vectors that SaveVectors took.
;
;NOTE : TSRs must BE AWARE that signal() functions which take these
;	vectors will be deactivated if the keep() function is executed.
;	If a TSR wants to use the signal functions when it is active it
;	will have to save/restore these vectors itself when activated and
;	deactivated.
;--------------------------------------------------------------------------
__restorezero	PROC	DIST
		PUBLIC	__restorezero
		ret
		push	ds
		mov	ax, 2500h
		lds	dx, _Int0Vector@
		int	21h
		pop	ds

		push	ds
		mov	ax, 2504h
		lds	dx, _Int4Vector@
		int	21h
		pop	ds

		push	ds
		mov	ax, 2505h
		lds	dx, _Int5Vector@
		int	21h
		pop	ds

		push	ds
		mov	ax, 2506h
		lds	dx, _Int6Vector@
		int	21h
		pop	ds

		ret
		ENDP

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

ErrorDisplay	PROC	NEAR
		mov	ah, 040h
		mov	bx, 2
		int	021h
		ret
ErrorDisplay	ENDP

_abort		PROC	DIST
		PUBLIC	_abort
		mov	cx, lgth_abortMSG
		mov	dx, offset DGROUP: abortMSG
MsgExit3	label	near
		mov	ds, cs: DGROUP@@
		call	ErrorDisplay
CallExit3	label	near
		mov	ax, 3
		push	ax
		call	__exit		 ; _exit(3);
		ENDP

; The DGROUP@ variable is used to reload DS with DGROUP

PubSym@ 	DGROUP@, <dw	?>, __PASCAL__



; __MMODEL is used to determine the memory model or the default
; pointer types at run time.

		public __MMODEL
__MMODEL	dw	MMODEL

_TEXT		ENDS

		SUBTTL	Start Up Data Area
		PAGE
;[]------------------------------------------------------------[]
;|	Start Up Data Area					|
;|								|
;|	WARNING 	Do not move any variables in the data	|
;|			segment unless you're absolutely sure	|
;|			that it does not matter.		|
;[]------------------------------------------------------------[]

_DATA		SEGMENT

;	Magic symbol used by the debug info to locate the data segment
		public DATASEG@
DATASEG@	label	byte

;	The CopyRight string must NOT be moved or changed without
;	changing the null pointer check logic

CopyRight	db	4 dup(0)
		db	'SDL compiler (c) -=Sap=-',0
lgth_CopyRight	equ	$ - CopyRight

CheckSum	equ	074Ah
NullCheck	db	'Null pointer assignment', 13, 10
lgth_NullCheck	equ	$ - NullCheck

ZeroDivMSG	db	'Divide error', 13, 10
lgth_ZeroDivMSG equ	$ - ZeroDivMSG

abortMSG	db	'Abnormal program termination', 13, 10
lgth_abortMSG	equ	$ - abortMSG

OriginalSP	dw	0
OriginalBP	dw	0
OriginalSS	dw	0
_ResultCode@	db	0
public		_ResultCode@

;
;			Interrupt vector save areas
;
;	Interrupt vectors 0,4,5 & 6 are saved at startup and then restored
;	when the program terminates.  The signal/raise functions might
;	steal these vectors during execution.
;
;	Note: These vectors save area must not be altered
;	      without changing the save/restore logic.
;
PubSym@ 	_Int0Vector	<dd	0>,		__CDECL__
PubSym@ 	_Int4Vector	<dd	0>,		__CDECL__
PubSym@ 	_Int5Vector	<dd	0>,		__CDECL__
PubSym@ 	_Int6Vector	<dd	0>,		__CDECL__
;
;			Miscellaneous variables
;
PubSym@ 	_C0argc,	<dw	0>,		__CDECL__
dPtrPub@	_C0argv,	0,			__CDECL__
dPtrPub@	_C0environ,	0,			__CDECL__
PubSym@ 	_envLng,	<dw	0>,		__CDECL__
PubSym@ 	_envseg,	<dw	0>,		__CDECL__
PubSym@ 	_envSize,	<dw	0>,		__CDECL__
PubSym@ 	_psp,		<dw	0>,		__CDECL__
PubSym@ 	_version,	<label word>,		__CDECL__
PubSym@ 	_osversion,	<label word>,		__CDECL__
PubSym@ 	_osmajor,	<db	0>,		__CDECL__
PubSym@ 	_osminor,	<db	0>,		__CDECL__
PubSym@ 	errno,		<dw	0>,		__CDECL__
PubSym@ 	_StartTime,	<dw   0,0>,		__CDECL__



;	Memory management variables

__nfile label word
		DW  20
		public	     __nfile
IF	LDATA  EQ  false
PubSym@ 	__heapbase,	<dw   DGROUP:edata@>,	__CDECL__
ENDIF
IFNDEF __HUGE__
PubSym@ 	__brklvl,	<dw   DGROUP:edata@>,	__CDECL__
ENDIF
PubSym@ 	_heapbase,	<dd   0>,	__CDECL__
PubSym@ 	_brklvl,	<dd   0>,	__CDECL__
PubSym@ 	_heaptop,	<dd   0>,	__CDECL__

;	If stack in DS and Large data model then override location of __emu


_DATA		ENDS


_CVTSEG 	SEGMENT
PubSym@ 	_RealCvtVector, <label	word>,	__CDECL__
		ENDS

_SCNSEG 	SEGMENT
PubSym@ 	_ScanTodVector,  <label word>,	__CDECL__
		ENDS

_BSS		SEGMENT
bdata@		label	byte
		ENDS

_BSSEND 	SEGMENT
edata@		label	byte
		ENDS

_STACK		SEGMENT
		db	128 dup(?)		 ;minimum stack size
		ENDS
		END	STARTX
