/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define KERNEL
#include "ixemul.h"
#undef u
#include <hardware/intbits.h>

#include <exec/memory.h>
#define BASE_EXT_DECL
#define BASE_PAR_DECL	struct ixemul_base *ixbase,
#define BASE_PAR_DECL0	struct ixemul_base *ixbase
#define BASE_NAME	ixbase->ix_intui_base
#include <inline/intuition.h>

/* #undef DEBUG */

#ifdef DEBUG
#define DP(a) kprintf a
#else
#define DP(a)
#endif

extern int __read(), __write(), __ioctl(), __fselect(), __close();
extern void trap_20 (), trap_00 (), launch_glue ();
extern int ix_timer();
extern int _dos20;

struct ixemul_base *
ix_open (struct ixemul_base *ixbase)
{
  /* here we must initialize our `user' structure */
  struct user *u;
  struct file *fin, *fout;
  /* an errno for those that later don't set it in ix_startup() */
  static int default_errno;
  int wb_started;
  int fd;
  BPTR fh;
  struct Task *me;
  struct ExecBase *SysBase;
  
  /* remember whether we were started by Workbench.
   * NOTE: you need to call ix_startup() to get all wb-startup things running
   *       ok!
   */
  SysBase = *(struct ExecBase **)4;
  me = SysBase->ThisTask;
  wb_started = ! ((struct Process *)me)->pr_CLI;

  u = (struct user *) AllocMem (sizeof (struct user), MEMF_PUBLIC|MEMF_CLEAR);
  if (u)  
    {
      /* from here on, every allocation is done thru {k}malloc(), and is
       * recorded thru the remember key in our {user,ix} structure */

      /* remember old state */
      u->u_otask_flags = me->tc_Flags;
      u->u_olaunch     = me->tc_Launch;
      u->u_otrap_code  = me->tc_TrapCode;
      u->u_otrap_data  = me->tc_TrapData;
      
      NewList (& u->u_md.md_list);

      me->tc_TrapData  = (APTR) u;
      me->tc_TrapCode  = (APTR) ((SysBase->AttnFlags & AFF_68020) ? 
      			         trap_20 : trap_00);

#if 0
DP(("ix_open: flags = $%lx, launch = $%lx, code = $%lx, data = $%lx, spreg = $%lx\n",
   u->u_otask_flags, u->u_olaunch, u->u_otrap_code, u->u_otrap_data, u->u_osp_reg));


DP(("SysBase->TDNestCnt = %ld, SysBase->IDNestCnt = %ld\n",
    SysBase->TDNestCnt, SysBase->IDNestCnt));
#endif

      /* setup the p_sigignore mask correctly */
      siginit (u);
      me->tc_SigRecvd &= 0x0fff;

      /* this library is a replacement for any c-library, thus we should be
       * started at the START of a program, and out of 16 available signals 
       * this calls has to simply succeed... I know I'm a lazy guy ;-) */
      u->u_sleep_sig   = AllocSignal (-1);

      me->tc_Launch    = launch_glue;
      me->tc_Flags    |= TF_LAUNCH;
      
      u->u_itimerint.is_Node.ln_Type = NT_INTERRUPT;
      u->u_itimerint.is_Node.ln_Name = me->tc_Node.ln_Name;
      u->u_itimerint.is_Node.ln_Pri  = 1;
      u->u_itimerint.is_Data         = (APTR) me;
      u->u_itimerint.is_Code	     = (APTR) ix_timer;
      AddIntServer (INTB_VERTB, & u->u_itimerint);
#if later
#endif

#ifdef DEBUG
      reset_watchdog();
#endif

      u->u_ixbase = ixbase;
      u->u_errno = &default_errno;
      u->u_mp = (struct MsgPort *) syscall (SYS_CreatePort, 0, 0);
      /* the CD storage. since 0 is a valid value for a lock, we use -1 */
      u->u_startup_cd = (BPTR)-1;
      
      if (u->u_mp)
        {
          u->u_time_req = (struct timerequest *)
	    syscall (SYS_CreateExtIO, u->u_mp, sizeof (struct timerequest));
	  
	  if (u->u_time_req)
	    {
	      if (!OpenDevice (TIMERNAME, UNIT_MICROHZ,
	      		       (struct IORequest *) u->u_time_req, 0))
	        {
	          /* NOTE: this stuff only works for CLI at this
		   *       point. ix_startup() will fill out the needed
		   *       stuff when it's called from a WB started
		   *       process 
		   */
		  if (! wb_started)
		    {
		      /*
		       * NOTE: if there's an error creating one of the standard
		       *       descriptors, we just go on, the descriptor in
		       *       question will then not be set up, no problem ;-)
		       */

		      if (! falloc (&fin, &fd))
		        {
		          if (fd != 0)
			    ix_panic ("allocated stdin is not fd #0!");
		       
		          if (! falloc (&fout, &fd))
		            {
			      if (fd != 1)
			        ix_panic ("allocated stdout is not fd #1!");

			      /* this opens stderr (if possible) */
			      syscall (SYS_open, "*", 2);

			      if (fh = Input ())
		                {
		                  fin->f_name = "<Standard Input>";
		                  fin->f_fh   = (struct FileHandle *)BTOCPTR(fh);
		                  __init_std_packet(&fin->f_sp);
		                  __fstat(fin);
			          fin->f_flags = FREAD|FEXTOPEN;
			          fin->f_type  = DTYPE_FILE;
			          fin->f_read  = __read;
			          fin->f_write = 0;
			          fin->f_ioctl = __ioctl;
			          fin->f_close = __close;
			          fin->f_select= __fselect;
			        }
			      else
			        {
			          u->u_ofile[0] = 0;
			          fin->f_count--;
		                }
			        
			      if (fh = Output ())
			        {
			          fout->f_name = "<Standard Output>";
			          fout->f_fh   = (struct FileHandle *)BTOCPTR(fh);
		                  __init_std_packet(&fout->f_sp);
		                  __fstat(fout);
			          fout->f_flags = FWRITE|FEXTOPEN;
			          fout->f_type  = DTYPE_FILE;
			          fout->f_read  = 0;
			          fout->f_write = __write;
			          fout->f_ioctl = __ioctl;
			          fout->f_close = __close;
			          fout->f_select= __fselect;
			        }
			      else
			        {
			          u->u_ofile[1] = 0;
			          fout->f_count--;
			        }
			    } /* falloc (&fout, &fd) */
			} /* falloc (&fin, &fd) */
		    } /* ! wb_started */
			  
		  return ixbase;
		}
	      /* couldn't open the timer device */
	      syscall (SYS_DeleteExtIO, u->u_time_req);
	    }
	  syscall (SYS_DeletePort, u->u_mp);
        }

      RemIntServer (INTB_VERTB, & u->u_itimerint);
      me->tc_Flags    = u->u_otask_flags;
      me->tc_Launch   = u->u_olaunch;
      FreeSignal (u->u_sleep_sig);

      /* all_free() MUST come before we remove the pointer to u */
      all_free ();
      me->tc_TrapCode = u->u_otrap_code;
      me->tc_TrapData = u->u_otrap_data;

      FreeMem (u, sizeof (struct user));
    }

  return 0;
}
