/*
 * __systemcalls.c - __syscall_start(), __syscall_end(), __syscall_init(),
 *                   __syscall_shutdown(), __exception_handler()

    This file is part of libRILc, a standard C library for GCC on Amiga OS.
    Copyright © 1998  Rask Ingemann Lambertsen

    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.

    As a special exception, if you link this library with files compiled
    with a GNU compiler to produce an executable, this does not cause the
    resulting executable to be covered by the GNU General Public License.
    This exception does not however invalidate any other reasons why the
    executable file might be covered by the GNU General Public License.
*/

/* The protocol: Before calling critical function calls (e.g. system
 * calls) that must not be interrupted by Ctrl-C and the like,
 * __syscall_start() should be called. __Syscall_start() will return an
 * object of type __syscall_handle. After the critical function call,
 * __syscall_end() should be called, with the __syscall_handle from the
 * __syscall_start as argument.
 *
 * Before the first __syscall_start() call, __syscall_init() should be called
 * to set things up for later __syscall_start() and __syscall_end() calls.
 * After the last __syscall_end() call, __syscall_shutdown() is called to
 * return things to a normal state.
 */

/* There isn't anything to protect the resource tracking system from
   on PowerUp boards. */

#ifndef __PPC__

#include <exec/types.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <signal.h>
#include "systemcalls.h"

static LONGBITS __exception_handler (LONGBITS exceptionBits __asm__("d0"),
                                     APTR     exceptionData __asm__("a1"))
{
  /* Check for Ctrl-E signal and map it to SIGKILL. */
  if (SIGBREAKF_CTRL_E & exceptionBits)
    raise (SIGKILL);

  /* Check for Ctrl-F signal and map it to SIGHUP. */
  if (SIGBREAKF_CTRL_F & exceptionBits)
    raise (SIGHUP);

  /* Check for Ctrl-C signal and map it to SIGINT. */
  if (SIGBREAKF_CTRL_C & exceptionBits)
    raise (SIGINT);

  /* Reenable the exceptions that occured. */
  return (exceptionBits);
}

/* Disable all exceptions in preparation for a system call (or other critical
   operation). */

__syscall_handle __syscall_start (VOID)
{
  return (SetExcept (0UL, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F));
}

/* Reenable the exceptions that were disabled by __syscall_start(). */
VOID __syscall_end (__syscall_handle sh)
{
  SetExcept (sh, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);
}

/* Install exception handler and enable exceptions. */
__syscall_handle __syscall_init (VOID)
{
  /* Turn off exceptions before installing handler. */
  SetExcept (0UL, 0xFFFFFFFF);
  /* Install exception handler. */
  FindTask (NULL)->tc_ExceptCode = __exception_handler;
  /* Enable exeptions on Ctrl-C/E/F. */
  SetExcept (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F,
             SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);

  return (0);
}

/* Remove exception handler, disable exceptions. */
VOID __syscall_shutdown (__syscall_handle sh)
{
  /* Turn off exceptions. */
  SetExcept (0UL, 0xFFFFFFFF);
  /* Remove exception handler. */
  FindTask (NULL)->tc_ExceptCode = NULL;
}

#endif /* NOT __PPC__ */
