/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
 * This file is part of the Linux-8086 C library and is distributed
 * under the GNU Library General Public License.
 */

#if !__FIRST_ARG_IN_AX__
#ifdef __AS386_16__
#ifdef __MSDOS__

#include <dos.h>
#include <fcntl.h>
#include <errno.h>
int errno;
unsigned _doserrno;

#ifdef L_dos_start

static char * defarg[2] = { "C" };
static char ** def_environ =defarg+1;
void (*__cleanup)() = 0;

#asm
  .data
export ___envseg
___envseg:
  .word 0

export ___psp
___psp:
  .word 0

  .text

export _exit
export __exit
_exit:			! exit(rv) function
  mov	bx,sp
  push	[bx+2]		! Copy the `rv` for the exit fuctions.
  mov	bx,[___cleanup] ! Call exit, normally this is `__do_exit`
  test	bx,bx
  je	no_clean	! But it`s default is null
  call	bx
no_clean:
  inc	sp
  inc	sp
__exit:			! _exit(rv)
  mov	bx,sp
  mov	ax,[bx+2]
  mov	ah,#$4c
  int	#$21
dos_1_exit:
  int	#$20

  .text
export ___cstartup	! Crt0 startup
___cstartup:
  mov	ax,#$3000	! Get DOS version
  int	$21
  cmp	al,#2		! DOS 2+ is Ok
  jb	dos_1_exit

  mov	dx,cs		! Current CS
  add	dx,#__segoff	! This var generated by the linker
  mov	ds,dx		! Correct DS

  mov	[___psp],es	! PSP segment
  seg	es
  mov	ax,[$2c]
  mov	[___envseg],ax	! Enviroment Segment

  			! Now need to free some RAM
  seg	es
  mov	bx,[2]		! Top of Ram
  mov	ax,ds
  add	ax,#4096	! Top of 64k data seg
  jc	use_tor		! Oops, wrapped
  cmp	ax,bx
  jnc	use_tor		! Bigger than tor
  mov	bx,ax
use_tor:
  mov	ax,cs		! Work out how big the memseg is needed
  sub	bx,ax
  mov	ah,#$4A		! Set it
  int	$21
  jnc	set_stack	! Good.
  ! Ooops, problem..
  ! BX is now how big it can be so set that.
  ! FIXME should check for BSS v Stack overlap
  mov	ah,#$4A
  int	$21

set_stack:		! Now set SS to the same as DS
  sub	bx,#__segoff	! And SP to the top of available memory.
  mov	cl,#4
  shl	bx,cl
  sub	bx,#2
  mov	ss,dx
  mov	sp,bx

zap_bss:		! Clear the BSS
  mov	es,dx		! ES now data seg
  mov	di,#__edata
  mov	cx,#__end
  sub	cx,di
  xor	ax,ax
  cld
  rep
   stosb

  push	[_def_environ]	! Defaults for when nothing is used.
  mov	ax,#_defarg
  push	ax
  mov	ax,#1
  push	ax

  mov	si,#auto_start	! Pointer to first autostart function
auto_run:
  mov	bx,[si]
  test	bx,bx
  jz	no_entry
  call	bx		! Call the function
no_entry:
  inc	si		! SI at next
  inc	si
  jmp	auto_run	! And round for the next.

call_exit:		! Last item called by above.
  pop	bx		! Be tidy.
  push	ax		! At the end the last called was main() push it`s
  call	_exit		! return val and call exit();
bad_exit:
  jmp	bad_exit	! Exit returned !!

  loc	2
  .word _main		! Segment 2 is the trailing pointers, main and the
  .word	call_exit	! routine to call exit.
data_start:

  .text

#endasm

__E_nosys()
{
#asm
   .text

export sys_call5
export sys_call4
export sys_call3
export sys_call2
export sys_call1
export sys_call0
sys_call5:		! Trap the unemulated Linux86 syscalls 
sys_call4:
sys_call3:
sys_call2:
sys_call1:
sys_call0:

#endasm
   errno = ENOSYS;
   return -1;
}
#endif

#ifdef L___mkargv

#ifdef __AS386_16__
#asm
  loc	1		! Make sure the pointer is in the correct segment
auto_func:		! Label for bcc -M to work.
  .word	___mkargv	! Pointer to the autorun function
  .text			! So the function after is also in the correct seg.
#endasm
#endif

void set_program_name(__argv)
char ** __argv;
{
    unsigned char *ptr;
    int src,len;

    __set_es(__envseg);
    src=0;
    while(__peek_es(src++)!=0)
    {
      while(__peek_es(src++)!=0);
    }
    src+=2; // step over 0x0001
    len=0;
    while (__peek_es(src+len++)!=0);
    ptr=sbrk(len);
    __argv[0]=ptr;
    while(len--)
    {
      *ptr++=__peek_es(src++);
    }
}

__mkargv(__argc, __argv)
int __argc;
char ** __argv;
{
   int length, i, argc=1, s=0;
   unsigned char *ptr, *p;
   int es=__get_es();

   set_program_name(__argv);

   __set_es(__psp);			/* Pointer to the args */
   length = __peek_es(0x80); 		/* Length of cmd line */
   if( length > 0 )
   {
      ptr = (char*) sbrk(length+1);	/* Allocate some space */

      for(i=0; i<length; i++)		/* Copy it in. */
      {
	ptr[i] = __peek_es(0x81+i);
	if( ptr[i] >  ' ' && s == 0 ) { argc++; s=1; }
	if( ptr[i] <= ' ' && s == 1 ) s=0;
      }
      ptr[length]=0;

      p= __argv[0];
      __argv = (char**) sbrk((argc+1)*sizeof(char*));
      __argv[0] = p;		/* TODO: The real command can be found */
      __argc=argc;

      /*
       * TODO: This needs to understand quoting and wildcards 
       */

      argc=1; s=0;
      for(i=0; i<length; i++)
      {
	if( ptr[i] >  ' ' && s == 0 ) { __argv[argc++] = ptr+i; s=1; }
	if( ptr[i] <= ' ' && s == 1 ) { ptr[i] = '\0'; s=0; }
      }
      __argv[argc] = 0;
   }
   __set_es(es);
}
#endif

#ifdef L___mkenvp

#ifdef __AS386_16__
#asm
  loc	1		! Make sure the pointer is in the correct segment
auto_func:		! Label for bcc -M to work.
  .word	___mkenvp	! Pointer to the autorun function
  .text			! So the function after is also in the correct seg.
#endasm
#endif

char ** environ = 0;

__mkenvp(__argc, __argv, __envp)
int __argc;
char ** __argv;
char ** __envp;
{
    /* Note: must write to __envp argument but not touch __argv or __argc */

    /* Note <stecdose@gmail.com>: we could copy array of strings to single
     * segment's space - this could take half of a segment.
     * as there are no far pointers in bcc we skip that space-wasting.
     * rather than this we walk along env pointed to in psp with "getenv".
     * If you're aware of another solution please let me know.
     */
    /*
    int save_es;

    save_es = __get_es();
    __set_es(__envseg);

    __set_es(save_es);
    */
}
#endif

#ifdef L_dos__fconv
/* This function converts filenames from unix like to DOS. */
char *
__fconv(fname)
char * fname;
{
static char buf1[66], buf2[66], *str = 0;
   register char *p, ch;
   int dot = 0;

   if( strcmp("/dev/tty", fname) == 0 ) return "CON:";

   if( str == buf1 ) str = buf2; else str = buf1;

   p = str;
   if( strncmp("/mnt/", fname, 5) == 0 )
   {
      strcpy(p, "A:"); p+=2; fname+=4;
   }
   /*
    * POSS:
    * open("/name/*", ...); looks for an environ var PATH_name=c:\x\y\z
    */

   while((ch = *fname++) && p < str+65)
   {
      if( ( ch >= 'a' && ch <= 'z' )
       || ( ch >= '0' && ch <= '9' )
       || ch == ':' || ch == '%' || ch == '-' || ch == '$' )
         ;
      else if( ch >= 'A' && ch <= 'Z' )
         ch = ch-'A'+'a';
      else if( ch == '.' && dot == 0 )
         dot = 1;
      else if( ch == '/' || ch == '\\' )
      {
         dot = 0; ch = '\\';
      }
      else ch = '_';

      *p++ = ch;
   }
   *p++ = '\0';
   return str;
}
#endif

#ifdef L___exterror

static char errno_xlate[] = {
   0, EINVAL, ENOENT, ENOENT, EMFILE, EACCES, EBADF, EFAULT, ENOMEM,
   EFAULT, ENOMEM, EINVAL, EINVAL, EINVAL, EINVAL, ENODEV, EPERM, EXDEV,
   ENFILE, EROFS, ENODEV, ENXIO, EINVAL, EIO, EINVAL, ESPIPE, EIO, EIO,
   EAGAIN, EIO, EIO, EIO, EBUSY, EBUSY, ENODEV, EFAULT, ENOLCK, EFAULT,
   EFAULT, ENOSPC
};

__exterror()
{
#asm
export exterror
exterror:
  push	ds
  push	es
  push	di
  push	si
  push	bp
  xor	bx,bx
  mov	ah,#$59
  int	$21
  pop	bp
  pop	si
  pop	di
  pop	es
  pop	ds
  mov	__doserrno,ax
#endasm
  {
     int nerrno;
     extern unsigned _doserrno;

     if( _doserrno == 0 )
        /* No error? No change. */;
     else if( _doserrno >= sizeof(errno_xlate)
           || errno_xlate[_doserrno] == EFAULT )
        errno = 16384+_doserrno;
     else
        errno = errno_xlate[_doserrno];
  }
  return -1;
}
#endif

#ifdef L_dos_read
int
read(fd, ptr, len)
int fd;
char *ptr;
unsigned len;
{
#asm
  mov	bx,sp
  mov	cx,[bx+6]
  mov	dx,[bx+4]
  mov	bx,[bx+2]
  mov	ah,#$3f
  int	#$21
  jnc	readok
  br	exterror
readok:
#endasm
}
#endif

#ifdef L_dos_write
int
write(fd, ptr, len)
int fd;
char *ptr;
unsigned len;
{
#asm
  mov	bx,sp
  mov	cx,[bx+6]
  mov	dx,[bx+4]
  mov	bx,[bx+2]
  mov	ah,#$40
  int	#$21
  jnc	writeok
  br	exterror
writeok:
#endasm
}
#endif

#ifdef L_dos_open
int
open(fname, type, cmode)
char * fname;
int type;
int cmode;
{
   register char * nname = __fconv(fname);
   int creat_mode = 0;
   int rv;

static int xlate_mode[] = {
#ifdef OPEN_LIKE_UNIX
   O_RDONLY|O_DENYNONE,
   O_WRONLY|O_DENYNONE,
   O_RDWR|O_DENYNONE,
#else
   O_RDONLY|O_DENYNONE,
   O_WRONLY|O_DENYWRITE,
   O_RDWR|O_DENYALL,
#endif
   3
};

   if( (cmode & 0222) == 0 ) creat_mode = 1;

   /* BzzzT. Ignore O_EXCL */

   if( type & O_TRUNC ) /* Assume TRUNC always means CREAT too */
      rv = __dos_creat(nname, creat_mode);
   else
   {
      int sv = errno;
      /* If we would open in compatibility mode make it a little more unixy */
      if( type & O_DENYMODE )
         rv = __dos_open(nname, type&(O_ACCMODE|O_DENYMODE|O_SETFD));
      else
         rv = __dos_open(nname, xlate_mode[type&O_ACCMODE]);

      if (rv == -1 && errno == ENOENT && (type & O_CREAT)) {
	 errno = sv;
	 rv = __dos_creat(nname, creat_mode);
      }
   }
   return rv;
}

__dos_open(fname, mode)
{
#asm
  mov	bx,sp
  mov	dx,[bx+2]               ;ds:dx points to source string
  mov	al,[bx+4]               ;access code
  mov	ah,#$3d                 ;ask for a open
  int	#$21
  jnc   openok                  ;return handle if no error
  br	exterror
openok: 
#endasm
}

__dos_creat(fname)
char * fname;
{
#asm
  mov     bx,sp
  mov     dx,[bx+2]               ;ds:dx points to source string
  xor     cx,cx                   ;normal attributes
  mov     ah,#$3c                 ;ask for a create
  int     #$21
  jnc     creok                   ;return handle if no error
  br	exterror
creok:
#endasm
}
#endif

#ifdef L_dos_close
close(fd)
{
#asm
  mov     bx,sp
  mov     bx,[bx+2]           ;file handle
  mov     ah,#$3e             ;ask for a close
  int     #$21
  mov     ax,0                ;return 0 if no error
  jnc     closeok
  br	exterror
closeok:
#endasm
}
#endif

#ifdef L_dos_unlink
unlink(fname)
char * fname;
{
#asm
  mov	bx,sp
  push	[bx+2]
  call	___fconv
  inc	sp
  inc	sp
  mov	dx,ax                   ;ds:dx points to source string
  mov	ah,#$41                 ;ask for a unlink
  int	#$21
  mov	ax,0                    ;assume no errors
  jnc	unlok
  br	exterror
unlok:
#endasm
}
#endif

#ifdef L_dos_lseek
long
lseek(fd, offset, mode)
int fd, mode;
long offset;
{
#asm
  mov     bx,sp
  mov     al,[bx+8]         ;mode of seek (0 to 2)
  mov     dx,[bx+4]         ;cx:dx is long offset
  mov     cx,[bx+6]
  mov     bx,[bx+2]         ;file handle
  mov     ah,#$42
  int     #$21              ;do the lseek
  jnc     seekok
  call	  exterror
  mov     dx,ax
seekok:
#endasm
}
#endif

#ifdef L_dos_segalloc
unsigned int
__segalloc(paracount)
unsigned int paracount;
{
#asm
  mov	bx,sp
  mov	bx,[bx+2]
  mov	ah,#$48
  int	$21
  jnc	ok
  mov	ax,#0
ok:
#endasm
}
#endif

#ifdef L_dos_segfree
unsigned int
__segfree(segno)
unsigned int segno;
{
#asm
  push	es
  mov	bx,sp
  mov	es,[bx+4]
  mov	ah,#$49
  int	$21
  jc	err
  mov	ax,#0
err:
  pop	es
#endasm
}
#endif

#ifdef L_dos_setvect
void
__setvect(i,j)
int i;
long j;
{
#asm
  mov	bx,sp
  mov	ax,[bx+2]
  mov	dx,[bx+4]
  mov	bx,[bx+6]
  push	ds
  test	bx,bx
  jnz	got_seg
  mov	bx,cs
got_seg:
  mov	ds,bx
  mov	ah,#$25
  int	$21
  pop	ds
#endasm
}
#endif

#ifdef L_dos_getvect
long
__getvect(vecno)
int vecno;
{
#asm
  mov	bx,sp
  mov	ax,[bx+2]
  mov	ah,#$35
  push	es
  int	#$21
  mov	dx,es
  mov	ax,bx
  pop	es
#endasm
}
#endif

#ifdef L_dos_getmod
int
__dos_getmod(fname)
{
#asm
#if __FIRST_ARG_IN_AX__
  mov	dx,ax
#else
  mov	bx,sp
  mov	dx,[bx+2]
#endif
  mov	ax,#$4300
  int	#$21
  jnc	statok
  br	exterror
statok:
  mov	ax,cx
#endasm
}
#endif

#ifdef L_dos_stat
int
__dos_stat(fname, dtaptr)
{
#asm
  mov	bx,sp
#if __FIRST_ARG_IN_AX__
  mov	cx,ax
  mov	dx,[bx+2]
#else
  mov	dx,[bx+4]
#endif
  mov	ah,#$1A		; Set DTA to requested
  int	#$21
#if __FIRST_ARG_IN_AX__
  mov	ax,cx
#else
  mov	dx,[bx+2]
#endif
  mov	ax,#$4300	; Locate the file
  int	#$21
  jc	nonesuch
  mov	ax,#$4e00	; Get all the available information.
  int	#$21
  jc	nonesuch
  xor	ax,ax
  ret
nonesuch:
  mov	ax,#2
  mov	_errno,ax
  mov	ax,#-1
#endasm
}
#endif

#ifdef L_dos_isatty
isatty(fd)
int fd;
{
#asm
  mov	bx,sp
  mov	bx,[bx+2]
  mov	ah,#$44
  mov	al,#0
  int	#$21
  xor	ax,ax
  test	dx,#$80
  jz	not_tty
  inc	ax
not_tty:
#endasm
}
#endif

#ifdef L_dos_abort
abort()
{
   write(2, "Abnormal program termination\r\n", 30);
   _exit(3);
}
#endif

#ifdef L_bdos
bdos(dosfn, dosdx, dosal)
int dosfn;
unsigned dosdx, dosal;
{
#asm
_bdosptr = _bdos
  mov	bx,sp
  push	si
  push	di

  mov	dx,_bdos.dosdx[bx]
  mov	cx,_bdos.dosfn[bx]
  mov	ax,_bdos.dosal[bx]
  mov	ah,cl

  int	$21

  pop	di
  pop	si
#endasm
}
#endif

#endif /* __MSDOS__ */
#endif /* __AS386_16__ */
#endif /* !__FIRST_ARG_IN_AX__ */

/*
# Name		No	Args	Flag, comment
CHDIR		12	1
TIME		13	1	* No long return val, arg _must_ exist.
MKNOD		14	3
CHMOD		15	2
CHOWN		16	3
BRK		17	1	* This is only to tell the system
STAT		18	2
GETPID		20	1	* This gets both pid & ppid
MOUNT		21	3	* Need more args & no ELKS
UMOUNT		22	1	. No ELKS
SETUID		23	1
GETUID		24	1	* This gets both uid and euid
STIME		25	2	. No ELKS should be 1 LONG arg
PTRACE		26	X	+
ALARM		27	2	?	No unused return.
FSTAT		28	2
PAUSE		29	0
UTIME		30	2
STTY		31	2	. ELKS ain't got this and it'll probably change
GTTY		32	2	. ELKS ain't got this and it'll probably change
ACCESS		33	2
NICE		34	1
FTIME		35	1	. ELKS ain't got this.
SYNC		36	0
KILL		37	2
RENAME		38	2
MKDIR		39	2
RMDIR		40	1
DUP		41	X	- Using nasty fcntl function
PIPE		42	1
TIMES		43	1
PROF		44	X	+
SETGID		46	1
GETGID		47	1	* This gets both gid and egid
SIGNAL		48	2	+
ACCT		51	X	+
PLOCK		53	X	+
IOCTL		54	3
FCNTL		55	3
EXEC		59	2	?
UMASK		60	1
CHROOT		61	1
SIGACTION	71	X
SIGSUSPEND	72	X
SIGPENDING	73	X
SIGPROCMASK	74	X
SIGRETURN	75	X
REBOOT		76	3	. No ELKS and the magic number will be diff.
*/

