#
! $Source$
! $State$
! $Revision$

! Declare segments (the order is important).

.sect .text
.sect .rom
.sect .data
.sect .bss

.sect .text

#define STACK_BUFFER 128 /* number of bytes to leave for stack */

begtext:
	! Make sure we are running under MS-DOS 2 or above.
	!
	! While at it, also remember the actual DOS version, so that we know
	! whether DOS gives us the program's name in the environment
	! segment.  (DOS 3+ does; DOS 2.x does not.)
	movb ah, 0x30
	int 0x21
	cbw
	cmpb al, 2
	xchg bp, ax
	jnc ok_sys

	mov dx, bad_sys_msg
dos_msg:
	movb ah, 9
	int 0x21
	ret

ok_sys:
	! Resize the program's memory control block (MCB) to cover only the
	! program's near code and data space.  Use the starting sp value as
	! a guide to how much memory we can grab.  Abort on any failure.
	!
	! As a side effect, this also frees up any memory allocated to our
	! program beyond 64 KiB.  (The freed memory can possibly be used by
	! e.g. child processes, in the future.)
	!
	! Also check that we have some space between the BSS end and the
	! starting sp.
	cmp sp, endbss+STACK_BUFFER
	jb no_room

	movb ah, 0x4a
	mov bx, sp
	movb cl, 4
	shr bx, cl
	inc bx
	int 0x21
	jc no_room

	! Clear BSS.
	mov di, begbss
	mov cx, endbss+1
	sub cx, di
	shr cx, 1
	xor ax, ax
	cld
	rep stosw

	! Get the size of the environment variables plus (if present) the
	! program name.  Also count the number of environment variables.
	xor di, di
	mov es, 0x002C(di)
	! ax = 0 from above
	cwd				! dx = count of env. vars.
	! cx = 0 from above
	dec cx				! cx = max. str. bytes to scan
	scasb				! handle special case of empty env.
	jz is_empty_env
size_env:
	inc dx
	repnz scasb
	scasb
	jnz size_env
is_empty_env:
	cmp bp, 2
	jz no_argv0
	scasw
	repnz scasb
no_argv0:

	! Copy out the environment variables and (possibly) program name
	! onto the stack.
	mov si, di
	dec si
	std
copy_env:
	and si, -2
	eseg lodsw
	push ax
	jnz copy_env
	mov cx, sp

	! Reset DF and es properly.
	cld
	push ss
	pop es

	! Reserve space for argc and the argv and envp pointers on the
	! stack.  These will be passed to __m_a_i_n later.
	sub sp, 6
	mov ax, sp

	! Build up argc, argv[], and envp[].
	push ax				! output buffer for argc, argv, envp
	push bp				! MS-DOS version
	push cx				! env. string data
	push dx				! count of env. vars.
	mov ax, 0x0080
	push ax				! raw command line
	call __sys_initmain
	add sp, 10

	! Bail out if something went wrong.
	test ax, ax
	jnz no_room

	! argc, argv, and envp are now at the stack top.  Now go.
	call __m_a_i_n
	add sp, 6
	push ax
	call _exit

no_room:
	mov dx, no_room_msg
	call dos_msg
	movb al, -1
	jmp al_exit

	! Exit.
.define __exit
.extern __exit
.define EXIT
.extern EXIT
__exit:
EXIT:
	pop bx
	pop ax
al_exit:
	movb ah, 0x4c
	int 0x21

! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.)

.define begtext, begdata, begbss
.sect .data;       begdata:
.sect .rom;        begrom:
.sect .bss;        begbss:

.sect .rom

! Some text messages.
bad_sys_msg: .ascii 'Bad DOS$'
no_room_msg: .ascii 'No room$'

! Some magic data. All EM systems need these.

.define .trppc, .ignmask, _errno
.comm .trppc, 4
.comm .ignmask, 4
.comm _errno, 4