/*
** CSPPC233Fix_wos.c
**
** An Instruction Access Exception handler, which tries to recover from
** a hardware bug on CSPPC/233 cards by fixing the trashed LR register.
**
** Written by Frank Wille <frank@phoenix.owl.de>.
** This code is PUBLIC DOMAIN.
**
*/

#include <stdio.h>
#include <time.h>
#include <dos/dos.h>
#include <powerpc/powerpc.h>
#include <powerpc/tasksPPC.h>
#include <clib/powerpc_protos.h>


static int sigExcept;                  /* exception notification signal */
static struct EXCContext EC;           /* copy of exception context */
static struct TaskPPC *thisTask;       /* this task */
static struct TaskPPC *excTask;        /* excepting task */

ULONG IAccessException(struct EXCContext *);

/* This line is vbcc-specific, the rest should be portable (to
   Storm-C, for example). Although I doubt that gcc-WarpOS would
   work, because the exception handler should be PowerOpen-ABI). */
ULONG r2(void) = "\tmr\tr3,r2"; /* gets TOC-pointer */



main()
{
  static ULONG cnt=0;
  struct TagItem ti[8];
  void *xhdl;
  ULONG sigmsk;

  if ((sigExcept = AllocSignalPPC(-1)) >= 0) {
    thisTask = FindTaskPPC(NULL);

    /* install a global handler for Instruction Access Exceptions */
    ti[0].ti_Tag = EXCATTR_CODE;
    ti[0].ti_Data = (ULONG)IAccessException;
    ti[1].ti_Tag = EXCATTR_DATA;
    ti[1].ti_Data = r2();
    ti[2].ti_Tag = EXCATTR_EXCID;
    ti[2].ti_Data = EXCF_IACCESS;
    ti[3].ti_Tag = EXCATTR_FLAGS;
    ti[3].ti_Data = EXCF_GLOBAL | EXCF_LARGECONTEXT;
    ti[4].ti_Tag = EXCATTR_PRI;
    ti[4].ti_Data = 127;
    ti[5].ti_Tag = EXCATTR_NAME;
    ti[5].ti_Data = (ULONG)"Trashed-LR bug fix for CSPPC/233";
    ti[6].ti_Tag = TAG_DONE;

    if (xhdl = SetExcHandler(ti)) {
      printf("Instruction Access exception handler installed.\n"
             "Press CTRL-C to quit.\n");
      do {
        /* wait for an exception or the signal to quit (CTRL-C) */
        sigmsk = WaitPPC((1L<<sigExcept) | (1L<<SIGBREAKB_CTRL_C));

        if (sigmsk & (1L<<sigExcept)) {
          /* print exception info */
          time_t tim;
          char buf[32];

          time(&tim);
          strftime(buf,sizeof(buf),"%x",localtime(&tim));
          printf("**(%lu) %s  Trashed LR: 0x%08lx  Task: %08lx (%s)\n",
                 ++cnt,buf,EC.ec_LR,(ULONG)excTask,
                 excTask->tp_Task.tc_Node.ln_Name);
        }
      }
      while (!(sigmsk & (1L<<SIGBREAKB_CTRL_C)));
      RemExcHandler(xhdl);
    }

    FreeSignalPPC(sigExcept);
  }
}


ULONG IAccessException(struct EXCContext *ec)
{
  /* first check if this exception was caused by a trashed LR register */
  if (ec->ec_UPC.ec_SRR0 == ec->ec_LR && (ec->ec_LR & 0xf0000000) != 0) {
    CopyMemPPC(ec,&EC,sizeof(struct EXCContext));  /* save exception ctxt. */
    ec->ec_UPC.ec_SRR0 &= 0x0fffffff;  /* fix PC and LR */
    ec->ec_LR &= 0x0fffffff;
    excTask = FindTaskPPC(NULL);
    SignalPPC(thisTask,1L<<sigExcept);  /* show exception info */
    return (EXCRETURN_ABORT);  /* retry instruction access */
  }
  return (EXCRETURN_NORMAL);
}
