File:  [OS/2 SDKs] / os2sdk / startup / dos / crt0.asm
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:25:13 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: os2sdk-1987, HEAD
Microsoft OS/2 SDK 12-15-1987

	page	,132
	TITLE	crt0 - C start up routine
;***
;crt0.asm - C start up routine
;
;	Copyright (c) 1985-1987, Microsoft Corporation.  All rights reserved.
;
;Purpose:
;	How startup works in a few words -
;
;	The startup and termination is performed by a few modules
;
;		crt0.asm	DOS 2.x/3.x specific init/term
;		crt0msg.asm	DOS 2.x/3.x error messages
;		(winstart.asm)	Windows specific init/term (not included)
;
;		crt0dat.asm	remainder of shared DOS 3.x init/term
;
;	*************  IMPORTANT  *****************************************
;
;	If the user reassembles this module, he will need to link using the
;	/DOSSEG switch or run the the DOSSEG.EXE program on crt0.obj, i.e.
;
;		dosseg crt0.obj
;
;	See the C documentation for more information about the /DOSSEG switch.
;
;	All assembler modules must be assembled with the /mx switch, i.e.
;
;		masm crt0/mx;
;
;*******************************************************************************


?DF=	1			; this is special for c startup
include	version.inc
.xlist
ifdef	_QC			;[5] move includes out of version
include	\sl\qc\src\include\kernel.inc	;[5]
include	\sl\qc\src\include\kmac.inc	;[5]
CEDEF	macro	name, num, msg		;;[8]
name	equ	num			;;[8]
endm					;;[8]
include \sl\qc\src\include\criterr.inc	;[8]
endif	;_QC			;[5] 

include	cmacros.inc
include	msdos.inc
include	brkctl.inc
.list

ifdef	_QC			;[2]
extrn	$Exec_Shell:FAR		;[9]
extrn	$GlobalAlloc:FAR	;[2]
extrn	$GlobalLock:FAR		;[2]
extrn	$GlobalUnlock:FAR	;[2]
extrn	_AddString:FAR		;[2]
endif	;_QC			;[2]

	page
;===========================================================================
;
;	Segment definitions
;
;	The segment order is essentially the same as in XENIX.
;	This module is edited after assembly to contain a dosseg comment
;	record for the linker.
;
;===========================================================================

createSeg _TEXT, code,	word,	public, CODE,	<>
createSeg C_ETEXT,etext, word,	public, ENDCODE,<>

createSeg _DATA, data,	word,	public, DATA,	DGROUP
createSeg STACK, stack,	para,	stack,	STACK,	DGROUP

defGrp	DGROUP			; define DGROUP

codeOFFSET equ	offset _TEXT:
dataOFFSET equ	offset DGROUP:

page

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

extrn	__acrtmsg:abs 		; trick to pull in startup messages


ifndef _QC
sBegin	stack
assumes	ds,data
	db	2048 dup (?)	; default stack size
sEnd
endif	;_QC

page

ifndef	_QC
externP	main			; C main program
endif	;_QC

externP	exit			; exit ( code )

if	sizeC
extrn	__exit:far 		; _exit ( code) (cmacros name conflict)
else
extrn	__exit:near
endif

sBegin	data

extrn	_edata:byte 		; end of data (start of bss)
extrn	_end:byte 		; end of bss (start of stack)

externW	_psp			; psp:0 (paragraph #)
externW	__argc
externDP __argv
externDP environ

ifdef _QC
externW	_QChdata		; High water mark of data space
globalW	_QCldata,0		; begining of DGroup (low water mark)

extrn	STKHQQ:WORD

extrn	__QCrtcsip:DWORD	;[2]
endif	;_QC

;	these are used by DOS C memory management (not used in Windows)

globalW	_asizds,0 		; DS size (in bytes)
globalW	_atopsp,0 		; top of stack (heap bottom)
globalW	_aexit_rtn,<codeoffset __exit> ; NEAR pointer

labelW	<PUBLIC,_abrktb> 	; segment table for brkctl
	dw	?
	dw	DGROUP
	db	(MAXSEG-1) * (size segrec) dup (?)

labelW	<PUBLIC,_abrktbe>
globalW	_abrkp,<dataoffset _abrktb>

sEnd

	page


externP	_cinit			; run-time initializers

ifdef _QC
externP _NMSG_TEXT		; pascal - find error message text
endif
externP _NMSG_WRITE		; pascal - write error message to stdout
externP	_FF_MSGBANNER 		; pascal - error message banner
				; (includes FORTRAN $DEBUG info)

externP	_setargv 		; process command line arguments
externP	_setenvp 		; process environment
externP	_nullcheck 		; check for null assignment


sBegin	code
assumes	cs,code

ifdef	_QC

	public	$F_PSP		; Segment of the fake PSP for QC
$F_PSP	dw	0		; Must be in CODE segment for recovery.
FirstTime db	1;		;[10] Flag to catch when the shell startup
				;[10]	code is executed.

assumes	ds,DGROUP		; DGROUP is setup and set by QC kernel
assumes	es,nothing
else	;_QC
assumes	ds,nothing
endif	;_QC

page
;***
;_astart - start of all C programs
;
;Purpose:
;	Startup routine to initialize C run-time environment
;
;Entry:
;
;Exit:
;	Exits to DOS via exit().
;
;Uses:
;
;Exceptions:
;
;*******************************************************************************

labelNP	<PUBLIC,_astart> 	; start address of all "C" programs

ifndef	_QC
;	check MS-DOS version for 2.0 or later

	callos	VERSION		; AX must be preserved until later
	cmp	al,2		; check for version 2 or later
	jae	setup		;   yes - continue with setup
	int	20h		; DOS 1.0 exit program	

setup:
endif	;_QC

ifdef	_QC
	mov	cs:[$F_PSP],es	; Save segment of fake PSP

	mov	di,ds		; di = DGROUP
	mov	si,es:[PSP_EOM_SEG] ; get max. paragraph
else	;_QC
	mov	di,DGROUP
	mov	si,ds:[DOS_MAXPARA] ; get max. paragraph
endif	;_QC

	sub	si,di		; si = # para in data area
	cmp	si,1000h	; if more than 64K
	jb	setSP

	mov	si,1000H	; use full 64K (-16)

setSP:

	cli			; turn off interrupts
	mov	ss,di		; SS = DGROUP
	add	sp,dataoffset _end-2 ; 2 for _asizds limit
	sti			; turn interrupts back on
	jnc	SPok

ifndef 	IBMC20
	push	ss		; establish DS=DGROUP for
	pop	ds		; _FF_MSGBANNER -> _NMSG_WRITE -> _NMSG_TEXT
endif	; IBMC20
ifdef	_QC			;[6]
	mov	es,cs:[$F_PSP]	;[9] find 'fake' PSP
	mov	es:[PSP_CRITERR], CE_EXEC; [9] Indicate an error to kernel
	jmp	Far Ptr $Exec_Shell ; [9]
	
else	;_QC			;[6]
	call	_FF_MSGBANNER	; for "\r\nrun-time error " banner
	xor	ax,ax		; stack overflow error message (0)
	push	ax
	call	_NMSG_WRITE
	mov	ax,DOS_terminate shl 8 + 255
	callos			; terminate process with 255
endif	;_QC			;[6]

SPok:
	assumes	ss,data

	and	sp,not 1	; make even (if not)
	mov	[_abrktb].sz,sp	; top DS free location
	mov	[_atopsp],sp	; save top of stack

ifdef	_QC
	mov	bx,sp		; Setup up a EOS flag on stack.
	sub	bx,es:[PSP_STK_SIZE]
	add	bx,2+256	;[1]add stack "slop" to keep 256 bytes
	mov	[STKHQQ],bx
	mov	Word Ptr [bx],5252h
endif	;_QC

	mov	ax,si		; si = # paragraphs
	mov	cl,4
	shl	ax,cl
	dec	ax
	mov	[_asizds],ax	; save DS size - 1 (in bytes)

ifdef	_QC
	mov	[__psp],es

	mov	[__QCldata],di		; Low end of memory for pointer checking

	mov	es,di			; es = DGROUP
	push	es			;[7] save DGROUP

	mov	ax,1			;[7] Now look for high end
	call	$GlobalLock		;[7] handle table
	mov	es,ax			;[7]
	mov	bx,0			;[7]
	mov	cx,128			;[7] 128 entries in table
	mov	ax,0			;[7] current high
	
ast1a:					;[7]
	cmp	ax,es:[bx]		;[7] is this high
	jae	ast1b			;[7] no
	mov	ax,es:[bx]		;[7] yes - new high
ast1b:
	add	bx,2			;[7] move to next entry
	loop	ast1a			;[7] loop until done

	mov	[_QChdata],ax		;[7] save as top of memory.
	pop	es			;[7] restore es=DGROUP
assumes	es,DGROUP

else	;_QC

;	release extra space to DOS

	add	si,di		; si = DGROUP + # para in DGROUP
	mov	ds:[DOS_MAXPARA],si ; fix psp:2
	mov	bx,es		; bx = PSP base
	sub	bx,si		; bx = - # para used
	neg	bx
	callos	setmem		; set memory block size
	mov	[_psp],ds	; save psp:0

;	zero data areas (_BSS and c_common)

	push	ss
	pop	es
	assumes	es,data

	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
	assumes	ds,data
endif	;_QC

;	do necessary initialization BEFORE command line processing!

	call	_cinit		; shared by DOS and Windows

	push	ss
	pop	ds		; ds = DGROUP
	assumes	ds,data

;	process command line and environment

ifdef	_QC			;[10]
	test	[FirstTime],1	;[10] Is this the first time?
	jz	setenv2		;[10] No  -  do the environment
	mov	es,[_psp]	;[11] look at fake psp to see if 
	test	es:[PSP_KERNEL_FLGS],PSP_KF_BATCH;[11] in batch mode?
	jz	setenv1		;[11] yes - skip parsing env
setenv2:
	call	_setenvp	;[10] crack enviornment
setenv1:			;[10]
	mov	[FirstTime],0	;[10] Never the first time again
else	;_QC			;[10]
	call	_setenvp	; crack environment
endif	;_QC			;[10]
	call	_setargv	; crack command line

;	call main and exit

	xor	bp,bp		; mark top stack frame for SYMDEB

if	sizeD
	push	word ptr [environ+2] ; the environment is not always in DS
endif
	push	word ptr [environ]

if	sizeD
	push	word ptr [__argv+2] ; the arguments are not always in DS
endif
	push	word ptr [__argv]

	push	[__argc]	; argument count

ifdef	_QC
	mov	es,[__psp]
	call	Dword Ptr es:[PSP_MAIN_ENTRY]
else	;_QC
	call	main		; main ( argc , argv , envp )
endif	;_QC

; use whatever is in ax after returning here from the main program

	push	ax
	call	exit		; exit (AX)
				;   _exit will call terminators

page
ifdef	_QC
labelFP <PUBLIC,_QCnptr_error>

; [2]
; We know we were called far, so to get the 'best guess' cs:ip, we
; just set __QCrtcsip by looking at our return address.
; [2]

	mov	ds,cs:[$F_PSP]		;[4] get PSP
	mov	ax,ds:[PSP_DGROUP_SEG]	;[4] get DGROUP
	mov	ds, ax			;[4] ds = DGROUP

	pop	word ptr [__QCrtcsip]	;[2] Get offset
	pop	word ptr [__QCrtcsip+2]	;[2] Get segment

	mov	ax,12		;Illegal Near pointer encountered.
	jmp	short _amsg_exit

labelFP <PUBLIC,_QCfptr_error>

; [2]
; We know we were called far, so to get the 'best guess' cs:ip, we
; just set __QCrtcsip by looking at our return address.
; [2]

	mov	ds,cs:[$F_PSP]		;[4] get PSP
	mov	ax,ds:[PSP_DGROUP_SEG]	;[4] get DGROUP
	mov	ds, ax			;[4] ds = DGROUP

	pop	word ptr [__QCrtcsip]	;[2] Get offset
	pop	word ptr [__QCrtcsip+2]	;[2] Get segment

	mov	ax,13		;Illegal Far pointer encountered.
	jmp	short _amsg_exit

labelFP	<PUBLIC,_Break_error>

; [2]
; We know we were called far, so to get the 'best guess' cs:ip, we
; just set __QCrtcsip by looking at our return address.
; [2]

	mov	ds,cs:[$F_PSP]		;[4] get PSP
	mov	ax,ds:[PSP_DGROUP_SEG]	;[4] get DGROUP
	mov	ds, ax			;[4] ds = DGROUP

	pop	word ptr [__QCrtcsip]	;[2] Get offset
	pop	word ptr [__QCrtcsip+2]	;[2] Get segment

	mov	ax,14		;Control BREAK encountered.
	jmp	short _amsg_exit
endif	;_QC

;***
;_amsg_exit, _cintDIV - Fast exit fatal errors
;
;Purpose:
;	Exit the program with error code of 255 and appropriate error
;	message.  cintDIV is used for integer divide by zero, amsg_exit
;	is for other run time errors.
;
;Entry:
;	AX	= error message number (amsg_exit only).
;
;Exit:
;	calls exit() [cintDIV] or indirect through _aexit_rtn [amg_exit].
;
;Uses:
;
;Exceptions:
;
;*******************************************************************************

labelNP	<PUBLIC,_cintDIV>

ifndef	IBMC20
	assumes ds,nothing
	assumes ss,data

endif ; IBMC20

ifdef	IBMC20
;	_NMSG_WRITE will reestablish ds = DGROUP
else
;	_cintDIV establishes ds = DGROUP
endif ; IBMC20

ifdef	_QC				;[3]
; [3]
; We got here from an interrupt so the stack looks like:
;   flags
;   seg
;   off
; So to get the CS:IP where it occurred, we just pop the seg and off from
; the stack (we don't need to get back...)
; [3]

	mov	ds,cs:[$F_PSP]		;[4] get PSP
	mov	ax,ds:[PSP_DGROUP_SEG]	;[4] get DGROUP
	mov	ds, ax			;[4] ds = DGROUP

	pop	word ptr [__QCrtcsip]	;[3] Offset
	pop	word ptr [__QCrtcsip+2]	;[3] Segment
else
ifndef	IBMC20
	mov	ax, DGROUP		; reestablish DS=DGROUP
	mov	ds, ax	
endif ; IBMC20
endif	;_QC				;[3]

	mov	ax,3		; Integer divide by zero interrupt
	mov	[_aexit_rtn],codeoffset _exit ; call high-level exit()
				; to cause file buffer flushing

labelNP	<PUBLIC,_amsg_exit>

ifdef	_QC			;[2]
	push	si		;[2] Is this necessary?
	push	di		;[2] Is this necessary?
	mov	si, ax		;[2] Save message number
endif	;_QC			;[2]

	push	ax		; message number for _NMSG_WRITE
	call	_FF_MSGBANNER	; run-time error message banner
	call	_NMSG_WRITE	; write error message to stdout

ifdef	_QC					;[2]
	mov	es, cs:[$F_PSP]			;[2] Find 'fake' PSP
	mov	es:[PSP_CRITERR], 255		;[2] Indicate error to kernel

	mov	ax, es:[PSP_ERRTAB_HDL]		;[2] Find error table handle
	or	ax, ax				;[2] Does it exist?
	jnz	_QC_0				;[2] Yes, don't allocate

	mov	ax, 26 * size error_struct	;[2]
	xor	dx, dx				;[2]
	xor	cx, cx				;[2]
	call	$GlobalAlloc			;[2]
	or	ax, ax				;[8] Check to see if we got it
	jz	_QC_Fail			;[8] Allocation failed

_QC_0:
	mov	di, ax				;[2] Save handle
	call	$GlobalLock			;[2] Handle should be in AX
	mov	es, ax				;[2] Get seg of memory block

	mov	ax, si				;[2] Get error number
	or	ax, 8000h			;[2] Set high bit in case 0
	mov	es:[es_ecode], ax		;[2]
	mov	ax, word ptr __QCrtcsip		;[2]
	mov	es:[es_oln], ax			;[2] Save IP (kludge)
	mov	ax, word ptr __QCrtcsip + 2	;[2]
	mov	es:[es_ob], ax			;[2] Save CS (kludge)

	push	es				;[2]

	mov	ax,255				;[2] 'run-time error' msg
	push	ax				;[2]
	call	_NMSG_TEXT			;[2] Fetch msg
	push	ax				;[2]
	call	_AddString			;[2] Add to strings heap
	add	sp, 2				;[2] C-calling convention
	cmp	ax, 0FFFFh			;[8] Error?
	jnz	_QC_2				;[8] No, continue

	pop	es				;[8]
	jmp	short _QC_Fail			;[8]

_QC_2:						;[8]
	xchg	si, ax				;[2] Save offset in heap
	push	ax				;[2] Retrieve message number
	call	_NMSG_TEXT			;[2] Return AX -> msg
	push	ax				;[2]
	call	_AddString			;[2] Add string to heap
	add	sp, 2				;[2] AddString is C-calling

	pop	es				;[2]
	cmp	ax, 0FFFFh			;[8] Error in AddString?
	jz	_QC_Fail			;[8] Yes, fail
	mov	es:[es_obString], si		;[2] Save offset of msg

	mov	es, cs:[$F_PSP]			;[2]
	mov	es:[PSP_ERRTAB_HDL], di		;[2]

	mov	ax, di				;[2] Retrieve handle
	call	$GlobalUnlock			;[2]
	jmp	short _QC_1			;[8] Exit cleanly

_QC_Fail:					;[8] Failure code
	mov	es, cs:[$F_PSP]			;[8]
	mov	es:[PSP_CRITERR], CE_NOMEM	;[8] Note true error

_QC_1:						;[8] Exit code
	pop	di				;[2] Necessary?
	pop	si				;[2] Necessary?
endif	;_QC					;[2]

	assumes	ds,data

	mov	ax,255
	push	ax
if	sizeC
	push	cs				; _exit is compiled far
						;	but called near
endif
	call	word ptr [_aexit_rtn] ; _exit(255) ordinarily
				; (or exit(255) for div by 0)
				; NEAR routine pointer

sEnd
	end	_astart		; start address

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.