/*
***************************************************************************
*
* Datei:
*    RSysSignalTrap.c
*
* Inhalt:
*    void HandleTrap(int signal);
*    void InstallTrapHandlers(void);
*    void RemoveTrapHandlers(void);
*    void _traphandler(long sig);
*
* Bemerkungen:
*    Verwaltung eines Traphandlers, um RSys wirklich (?) absturzsicher
*    zu machen.
*
* Erstellungsdatum:
*    07-Jul-93    Rolf Böhme
*
* Änderungen:
*    07-Jul-93    Rolf Böhme    Erstellung
*
***************************************************************************
*/

#include "RSysDebug.h"
#include "RSysFunc.h"

/*
#define SIG_BEGINSSP       0
#define SIG_BEGINPC        1
#define SIG_BUSERROR       2
#define SIG_ADDRERROR      3
#define SIG_ILLCOMM        4
#define SIG_DIVZERO        5
#define SIG_CHKERROR       6
#define SIG_TRAPV          7
#define SIG_VIOLPRIV       8
#define SIG_TRACE          9
#define SIG_LINEAEMUL      10
#define SIG_LINEFEMUL      11
#define SIG_NOTDEF         12
#define SIG_COPROPROT      13
#define SIG_FORMATERR      14
#define SIG_INTRFAIL       15

#define SIG_MC_ILLCOND     48
#define SIG_MC_ILLRESULT   49
#define SIG_MC_DIVZERO     50
#define SIG_MC_UNDERFLOW   51
#define SIG_MC_OPERROR     52
#define SIG_MC_OVERFLOW    53
#define SIG_MC_NANSIG      54
#define SIG_MC_NOTDEF      55
*/

#define _NUMSIG            15
#define _FSTSIG            0

void (*mysignal(register int sig, void (*func)(int)))(int);
void my_sig_setup(void);

void (*_sigfuncs[_NUMSIG])(int);
int _trapexception(void);
void *_oldtrap, **_trapaddr;

static struct Task *tp;

char *TrapCPUMsg[] = {
   "Begin of SSP (ISP by 680x0)",
   "Begin of PC",
   "Bus error",
   "Address error",
   "Illegal or unknown instruction",
   "Division by zero or arithmetic fault",
   "CHK and CHK2 affected",
   "TRAP or TRAPV reached",
   "Priviledge violation",
   "Trace error",
   "Line A Emulator, illegal Opcode by MC68000",
   "Line F Emulator, illegal Opcode by MC68000",
   "Error is reserved",
   "Coprocessor protocol error",
   "Format error by MC680x0",
   "Uninitialized Interrupt"
};

   /*
    * convert() konvertiert eine 32Bit-Zahl in einen
    * "Binärstring", also in einen String, der nur aus '0'
    * und '1' besteht
    */
static void
convert(ULONG state,char *str)
{
   register int bit;

   for( bit = 0; bit < 32; bit++ )
      str[31-bit] = ((state & (1L<<bit)) ? '1' : '0');

   str[32] = STRINGEND;
}

   /*
    * HandleTrap() ist die Funktion, die im Falle eines
    * System traps aktiviert wird. Diese Routine ruft
    * entweder den System debugger ROMWack auf, oder
    * verursacht einen Kaltstart des Rechners, falls dies
    * gewünscht wird. Installiert wird diese Routine
    * mit der ANSI-Routine signal() des
    * Aztec-C-Compilers
    */
void
HandleTrap(int signal)
{
   int action;
   char exceptstr[33],signalstr[33],statestr[33];
   UBYTE *fmt = (UBYTE *)"Error signal: %s (%ld)\n"
	                      "Exception   : %s (0x%08lX)\n"
	                      "Task signal : %s (0x%08lX)\n"
	                      "CPU Status  : %s (0x%08lX)\n"
                         "Trap data   : %08lX\n"
                         "Except data : %08lX\n";
   ULONG exc,sig,sta;
   APTR UserStack;

   UserStack = SuperState();

   exc = SetExcept(0L,0L);
   sig = SetSignal(0L,0L);
   sta = SetSR(0L,0L);

   UserState(UserStack);

   convert(exc,exceptstr);
   convert(sig,signalstr);
   convert(sta,statestr);

   if(IntuitionBase != NULL)
	   action = MyEasyRequest(NULL,
	                 (UBYTE *)NAME " *** Emergency trap failure message ***",
	                 (UBYTE *)"Continue|Terminate RSys|Call system debugger|Reboot system",
	                 fmt, TrapCPUMsg[signal], signal,
	                 exceptstr,exc,signalstr,sig,statestr,sta,
                    tp->tc_TrapData, tp->tc_ExceptData);
   else
   {
      action = 2;
	   Printf(fmt,(ULONG)TrapCPUMsg[signal],signal,
	              exceptstr,exc,signalstr,sig,statestr,sta,
                 tp->tc_TrapData, tp->tc_ExceptData);
   }

   switch(action)
   {
      case 0:
         ColdReboot();
         break;

      case 1:
         break;

      case 2:
         CloseAll();
         break;

      case 3:
         Debug(0L);
         CloseAll();
         break;
   }

   return;
}

   /*
    * InstallTrapHandlers() installiert einen Traphandler,
    * der auf verschiedene Traps reagieren kann
    */
void
InstallTrapHandlers(void)
{
   register int i;
   extern struct ExecBase *SysBase;

   tp = FindTask(NULL);

   _trapaddr = (void *)&tp->tc_TrapCode;
   _oldtrap= tp->tc_TrapCode;

   tp->tc_TrapCode = (APTR)&_trapexception;

   if (SysBase->AttnFlags & AFF_68881)
   {
#asm
        mc68881

        fmove.l  FPCR,d0
        or.w     #$1400,d0
        fmove.l  d0,FPCR
#endasm
    }

   for(i = _FSTSIG; i <= _NUMSIG; i++)
      _sigfuncs[i] = HandleTrap;

   return;
}

void
RemoveTrapHandlers(void)
{
   register int i;

   Forbid();

   if (_trapaddr)
      *_trapaddr = _oldtrap;

   for(i = _FSTSIG; i <= _NUMSIG; i++)
      _sigfuncs[i] = NULL;

   Permit();

   return;
}

void
_traphandler(long sig)
{
   register void (*handler)(int);

   if ((handler = _sigfuncs[sig]) != NULL)
      (*handler)(sig);

   return;
}

#asm
        far      code

        public   _geta4
        public   __trapexception

DEFTRAP MACRO                       ;&signalnumber
        cmp.w    \1,d0             ;compare trap number
        beq      _deftrap           ;branch to default traphandler
        ENDM

MYTRAP  MACRO                       ;&signalnumber
        cmp.w    \1,d0             ;compare trap number
        beq      _mytrap            ;branch to own traphandler
        ENDM

__trapexception:
        movem.l  d0/a4,-(sp)        ;need some registers
        jsr      _geta4             ;set up a4
        move.l   8(sp),d0           ;get trap number
        DEFTRAP  #1                 ;define the traphandler
        DEFTRAP  #2
        DEFTRAP  #3
        MYTRAP   #4
        MYTRAP   #5
        MYTRAP   #6
        DEFTRAP  #7
        MYTRAP   #8
        MYTRAP   #9
        DEFTRAP  #10
        DEFTRAP  #11
        MYTRAP   #12
        DEFTRAP  #13
        DEFTRAP  #14
        DEFTRAP  #15
        MYTRAP   #16

_deftrap:
        move.l   4(sp),d0           ;get a4 for later
        move.l   __oldtrap,4(sp)    ;get regular handler
        move.l   d0,a4              ;set real a4 value
        move.l   (sp)+,d0           ;restore d0 contents
        rte                         ;jump to it

_mytrap:
        move.l   a0,-(sp)           ;save a0
        move.l   usp,a0             ;get user stack pointer
        move.l   18(sp),-(a0)       ;save old pc
        move.w   14(sp),-(a0)       ;save old sr
        movem.l  d0-d7/a0-a6,-(a0)  ;save old registers
        movem.l  (sp)+,d1/d2/d3     ;restore d0/a0/a4
        move.l   d1,(a0)            ;set d0
        move.l   d2,32(a0)          ;set a0
        move.l   d3,48(a0)          ;set a4
        move.l   d0,-(a0)           ;pass trap number on stack
        lea      _conttrap,a1       ;get return in case they want to continue
        move.l   a1,-(a0)           ;set return address
        move.l   a0,usp             ;set new user stack pointer
        lea      __traphandler,a0   ;get trap handler
        move.l   a0,6(sp)           ;modify rte address
        addq     #4,sp              ;remove trap #
        rte                         ;go and do the handler

_conttrap:
        add.w    #4,sp              ;pop arg
        movem.l  (sp)+,d0-d7/a0-a6  ;restore old registers
        move.w   (sp)+,ccr          ;restore old condition codes
        rts                         ;and continue where we left off
#endasm

