/* Copyright 1992-1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_executor[] =
	    "$Id: executor.c 88 2005-05-25 03:59:37Z ctm $";
#endif

#define DEBUG

#include "rsys/common.h"

#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "ResourceMgr.h"
#include "SegmentLdr.h"
#include "MemoryMgr.h"
#include "StdFilePkg.h"
#include "EventMgr.h"
#include "VRetraceMgr.h"
#include "OSUtil.h"
#include "FontMgr.h"
#include "ScrapMgr.h"
#include "ToolboxUtil.h"
#include "FileMgr.h"
#include "ControlMgr.h"
#include "DeviceMgr.h"
#include "SoundDvr.h"
#include "PrintMgr.h"
#include "StartMgr.h"
#include "CommTool.h"

#include "rsys/trapglue.h"
#include "rsys/cquick.h"
#include "rsys/file.h"
#include "rsys/next.h"
#include "rsys/soundopts.h"
#include "rsys/pstuff.h"
#include "rsys/jumpvectors.h"
#include "rsys/prefs.h"
#include "rsys/aboutpanel.h"
#include "rsys/segment.h"
#include "rsys/misc.h"
#include "rsys/host.h"
#include "rsys/executor.h"
#include "rsys/hfs.h"
#include "rsys/float.h"
#include "rsys/vdriver.h"
#include "rsys/trapname.h"

#include "rsys/options.h"
#include "rsys/license.h"
#include "rsys/suffix_maps.h"
#include "rsys/string.h"
#include "rsys/custom.h"

#undef PRIVATE
#define PRIVATE
#define PUBLIC

#define FASTALINETRAPS

/*
 * For now the NEXT handles A-line traps differently than on the Sun.
 * The NEXT will bypass the A-line and priv instruction dispatches if
 * possible (meaning if there's no page fault during the dispatch).  If
 * not possible, we get here and are expected to force the page fault
 * ourselves so we can go back into kernel mode.  Pretty bizzare, eh?
 */

#if defined(NEXT) && !defined(SYN68K)
PRIVATE void emthandler(LONGINT wsignal, LONGINT code, struct sigcontext *scp)
{
    LONGINT saved2;
    volatile LONGINT temp;
    static int beenhere = 0;


    if (!beenhere) {
	ROMlib_load_ardi_mods();
	beenhere = 1;
    }

    asm("movel d2, %0" : "=g" (saved2));

    temp  = *(volatile LONGINT *) 0x5C;	/* will get page 0 and the stack */

    asm("movel %0, d2" : : "g" (saved2));
}
#endif /* NEXT && !SYN68K */


LONGINT debugnumber, debugtable[1<<12], cutoff = 20;

#if !defined (MSDOS) && !defined (NDEBUG)
typedef struct
{
  unsigned trapno;
  int32 when;
} trap_when_t;


static int
compare_trap_when (const void *p1, const void *p2)
{
  return ((const trap_when_t *)p1)->when - ((const trap_when_t *) p2)->when;
}


/* Prints out the traps executed within the last NUM_TRAPS_BACK traps. */
void
dump_recent_traps (int num_traps_back)
{
  trap_when_t traps[0x1000];
  int i, num_interesting_traps;

  /* Record all the traps that are recent enough. */
  for (i = 0, num_interesting_traps = 0; i < (int) NELEM (traps); i++)
    {
      int32 when;

      when = debugtable[i];
      if (when != 0 && when >= debugnumber - num_traps_back)
	{
	  traps[num_interesting_traps].trapno = 0xA000 + i;
	  traps[num_interesting_traps].when = when;
	  num_interesting_traps++;
	}
    }

  /* Sort them by time, oldest first. */
  qsort (traps, num_interesting_traps, sizeof traps[0], compare_trap_when);

  /* Print them out. */
  for (i = 0; i < num_interesting_traps; i++)
    printf ("0x%04X\t%ld\t%s\n", traps[i].trapno, (long) traps[i].when,
	    trap_name (traps[i].trapno));
}
#endif /* !MSDOS && !NDEBUG */



#if !defined (NDEBUG)

char trap_watchpoint_data[1024];
char *trap_watchpoint_name[1024];
struct
{
  /* pointer to actual memory */
  int handle_p;
  char *x;
  /* pointer to our `orignal' copy */
  char *orig;
  int size;
} trap_watchpoints[1024];
int trap_watchpoint_next_data;
int trap_watchpoint_next;


/* make a per-trap watchpoint of the data at `x', of size `size' */
void
trap_watch (char *name, void *x, int size)
{
  char *trap_data;
  
  if (trap_watchpoint_next == 1024)
    {
      fprintf (stderr, "all available trap watchpoints used\n");
      return;
    }
  trap_watchpoints[trap_watchpoint_next].size = size;
  trap_watchpoints[trap_watchpoint_next].handle_p = FALSE;
  trap_watchpoints[trap_watchpoint_next].x = x;
  trap_data
    = trap_watchpoints[trap_watchpoint_next].orig
    = &trap_watchpoint_data[trap_watchpoint_next_data];
  trap_watchpoint_next ++;
  trap_watchpoint_next_data += size;

  memcpy (trap_data, x, size);
}

void
trap_watch_handle (char *name, Handle x, int size)
{
  char *trap_data;
  
  if (trap_watchpoint_next == 1024)
    {
      fprintf (stderr, "all available trap watchpoints used\n");
      return;
    }
  trap_watchpoints[trap_watchpoint_next].size = size;
  trap_watchpoints[trap_watchpoint_next].handle_p = TRUE;
  trap_watchpoints[trap_watchpoint_next].x = (void *) x;
  trap_data
    = trap_watchpoints[trap_watchpoint_next].orig
    = &trap_watchpoint_data[trap_watchpoint_next_data];
  trap_watchpoint_next ++;
  trap_watchpoint_next_data += size;

  memcpy (trap_data, STARH ((Handle) x), size);
}

void
trap_break_me (void)
{
  
}

void
trap_dump_bits (char *msg, char *data, int size)
{
  int i;

  fprintf (stderr, "%s: ", msg);
  for (i = 0; i < size; i ++)
    fprintf (stderr, "%x%x",
	     (int) ((data[i] >> 4) & 0xF),
	     (int) (data[i] & 0xF));
  fprintf (stderr, "\n");
}


int check_trap_watchpoints_p = FALSE;

void
check_trap_watchpoints (char *msg)
{
  int size, i;
  
  for (i = 0; i < 1024; i ++)
    {
      if (trap_watchpoints[i].x)
	{
	  void *x;
	  
	  x = (trap_watchpoints[i].handle_p
	       ? (void *) STARH ((Handle) trap_watchpoints[i].x)
	       : trap_watchpoints[i].x);
	      
	  size = trap_watchpoints[i].size;
	  
	  if (memcmp (x, trap_watchpoints[i].orig,
		      size))
	    {
	      /* differs */
	      fprintf (stderr, "%s", msg);
	      trap_dump_bits ("old", trap_watchpoints[i].orig, size);
	      trap_dump_bits ("new", x, size);
	      
	      memcpy (trap_watchpoints[i].orig, x, size);
	      
	      trap_break_me ();
	    }
	}
    }
}
#endif /* !NDEBUG */

PRIVATE void setup28( void )
{
#if defined (NEXT) && !defined (SYN68K)
#if 1
    static LONGINT trap_dispatcher_callback = -1;

    if (trap_dispatcher_callback == -1)
      trap_dispatcher_callback = callback_install (aline_stub, NULL);
    *(LONGINT *) 0x28 = trap_dispatcher_callback;
#else
    *(LONGINT *) 0x28 = (LONGINT) trapdispatcher;
#endif

    *(LONGINT *) 0x20 = (LONGINT) privdispatcher;
    *(LONGINT *) 0x5C = (LONGINT) T('A','R','D','I');
#endif /* NEXT && !SYN68K */
}

PUBLIC void setupsignals( void )
{
#if defined (NEXT)
    LONGINT tocatch[] = {
	SIGHUP,
	SIGINT,
	SIGQUIT,
	SIGILL,
	SIGTRAP,
	SIGIOT,
	SIGEMT,
	SIGFPE,
	SIGBUS,
	SIGSEGV,
	SIGSYS,
	SIGPIPE,
	SIGTERM,
	SIGXCPU,
	SIGXFSZ,
	SIGVTALRM,
	SIGPROF,
	SIGUSR1,
	SIGUSR2,
	0
    }, *tocatchp;
#endif /* NEXT */
#if !defined (FASTALINETRAPS) || defined (NEXT)
#if defined(USE_BSD_SIGNALS)
    typedef struct sigvec sigvec_t;
    typedef struct sigstack sigstack_t;
#endif
#endif /* FASTALINETRAPS || NEXT */

#if (!defined (FASTALINETRAPS) || defined (NEXT)) && !defined (SYN68K)
    {
      sigstack_t sigstackarg;
      sigvec_t sigvecarg;
      static char signalstack[3*1024];

      sigstackarg.ss_sp = signalstack + sizeof(signalstack);
      sigstackarg.ss_onstack = 0;
      sigstack(&sigstackarg, (sigstack_t *) 0);
      sigvecarg.sv_handler = (void *) emthandler;
      sigvecarg.sv_mask = 0;
      sigvecarg.sv_flags = SV_ONSTACK;
      sigvec(SIGEMT, &sigvecarg, (sigvec_t *) 0);
    }
#endif /* (FASTALINETRAPS || NEXT) && !SYN68K */
    setup28();
}

#define TOOLMASK		(0x3FF)
#define OSMASK			(0xFF)
#define DONTSAVEA0BIT		(0x100)
#define POPBIT			(0x400)

#define LOADSEGTRAPWORD		(0xA9F0)
#define EXITTOSHELLTRAPWORD	(0xA9F4)
enum { MODESWITCHTRAPWORD = 0xaafe };

#define ALINEEXCEPTIONFRAMESIZE	8
#define WEIRDMAGIC	(0x1F52)

/*
 * FAKEPascalToCCall pushes a phoney return address and then calls
 * PascalToCCall and throws away the return value.  It will be something
 * like:
 *
 */

#define FAKEPascalToCCall(xxx) \
	do {PUSHADDR(0xEACE4321); (void) PascalToCCall(0x87654231, xxx); } while (0)

unsigned short mostrecenttrap;

/* #define MEMORY_WATCH */

#include "rsys/mman_private.h"

#if defined (MEMORY_WATCH)

int memory_watch = 0;

PRIVATE void
dump_difference (uint16 trapn, int i,
		 zone_info_t *currentp, const zone_info_t *newp)
{
  fprintf (stderr, "%d %s(%d): D#rel = %d, D#nrel = %d, D#free = %d, Dtotal = %d\n",
	   debugnumber, trap_name (trapn), i, newp->n_rel - currentp->n_rel,
	  newp->n_nrel - currentp->n_nrel, newp->n_free - currentp->n_free,
	  newp->total_free - currentp->total_free);
  *currentp = *newp;
}

PRIVATE void
compare_zone_infos (uint16 trapn, zone_info_t current[3], zone_info_t new[3])
{
  int i;

  for (i = 0; i < 2; ++i)
    {
      if ((ABS (current[i].n_rel      - new[i].n_rel ) > 2) ||
	  (ABS (current[i].n_nrel     - new[i].n_nrel) > 2) ||
	  (ABS (current[i].total_free - new[i].total_free) > 12 * 1024))
	dump_difference (trapn, i, &current[i], &new[i]);
    }
}
#endif

PUBLIC syn68k_addr_t alinehandler(syn68k_addr_t pc, void *ignored)
{
    syn68k_addr_t retval;
    unsigned short trapno, status;
    uint32 savea0, savea1, savea2, saved1, saved2;
    unsigned short trapword;
    void *togoto;	/* FIXME: really a syn68k_addr_t */
#if defined (MEMORY_WATCH)
    zone_info_t current_zone_infos[3];
    zone_info_t new_zone_infos[3];
#endif

#if defined (MEMORY_WATCH)
    if (memory_watch)
      {
	ROMlib_sledgehammer_zones (__PRETTY_FUNCTION__, __FILE__, __LINE__,
				   "after aline", current_zone_infos);
      }
#endif


    mostrecenttrap = trapword = READUW(pc);
    retval = pc + 2;

#if !defined (NDEBUG)
    if (check_trap_watchpoints_p)
      check_trap_watchpoints ("entering `alinehandler ()'\n");
#endif 

/*
 * Code for debugging
 */

#if !defined (NDEBUG)
    debugtable[trapword&0xFFF] = ++debugnumber;
#endif /* !NDEBUG */

#if 0
    warning_trace_info ("in trapword = 0x%x, pc = 0x%x", trapword, pc);
#endif

    status = READUW(EM_A7);
    EM_A7 += ALINEEXCEPTIONFRAMESIZE;
    if (trapword & TOOLBIT) {
	trapno = trapword & TOOLMASK;
	togoto = tooltraptable[trapno];
	if (trapword & POPBIT)
	    retval = POPADDR();
	if (togoto == toolstuff[trapno].orig) {
	    if (toolstuff[trapno].ptoc.magic == (ULONGINT) -1) {
	        syn68k_addr_t new_addr;

		PUSHADDR(0x80014321);	/* better not use this */
		new_addr =
		  (*(callback_handler_t)toolstuff[trapno].ptoc.wheretogo)
							      (pc, (void *) 0);
		if (trapword == LOADSEGTRAPWORD)
		    retval -= 6;
		else if (trapword == MODESWITCHTRAPWORD)
		    retval = new_addr;
	    } else
		FAKEPascalToCCall(&toolstuff[trapno].ptoc);
	} else {
	    PUSHADDR(retval);			/* Where they'll return to */
	    retval = (syn68k_addr_t) togoto;	/* Where they have patched */
							    /* us to go to */
	}
    } else {
	trapno = trapword & OSMASK;
	togoto = ostraptable[trapno];
	if (togoto == osstuff[trapno].orig) {
	    saved1 = EM_D1;
	    saved2 = EM_D2;
	    savea1 = EM_A1;
	    savea2 = EM_A2;
	    EM_D1 = trapword;
	    EM_D2 = (trapword & 0xFF) << 2;
	    EM_A2 = (LONGINT) togoto;
	    savea0 = EM_A0;
	    PUSHADDR(0x88A84321);	/* better not use this */
	    (*(callback_handler_t)osstuff[trapno].func)(pc,
				    callback_argument((syn68k_addr_t) togoto));
	    if (!(trapword & DONTSAVEA0BIT))
		EM_A0 = savea0;
	    EM_D1 = saved1;
	    EM_D2 = saved2;
	    EM_A1 = savea1;
	    EM_A2 = savea2;
	} else {
	    PUSHADDR(retval);
	    PUSHUW(status);
	    PUSHUW(WEIRDMAGIC);
	    PUSHUL(EM_A2);
	    PUSHUL(EM_D2);
	    PUSHUL(EM_D1);
	    PUSHUL(EM_A1);
	    EM_D1 = trapword;
	    EM_D2 = (trapword & 0xFF) << 2;
	    EM_A2 = (LONGINT) togoto;
	    if (!(trapword & DONTSAVEA0BIT))
		PUSHUL(EM_A0);
	    CALL_EMULATOR((syn68k_addr_t) togoto);
	    if (!(trapword & DONTSAVEA0BIT))
		EM_A0 = POPUL();
	    EM_A1 = POPUL();
	    EM_D1 = POPUL();
	    EM_D2 = POPUL();
	    EM_A2 = POPUL();
	    EM_A7 += 4;
	    retval = POPADDR();
	}
	cpu_state.ccc = 0;
	cpu_state.ccn = (cpu_state.regs[0].sw.n < 0);
	cpu_state.ccv = 0;
	cpu_state.ccx = cpu_state.ccx;	/* unchanged */
	cpu_state.ccnz = (cpu_state.regs[0].sw.n != 0);
    }

#if defined (MEMORY_WATCH)
    if (memory_watch)
      {
	ROMlib_sledgehammer_zones (__PRETTY_FUNCTION__, __FILE__, __LINE__,
				   "after aline", new_zone_infos);
	compare_zone_infos (trapword, current_zone_infos, new_zone_infos);
      }
#endif

#if !defined (NDEBUG)
    if (check_trap_watchpoints_p)
      check_trap_watchpoints ("exiting `alinehandler ()'\n");
#endif 

#if 0
    warning_trace_info ("out retval = 0x%x", retval);
#endif

    return retval;
}

PUBLIC void executor_main( void )
{
    char quickbytes[grafSize];
    LONGINT tmpA5;
    INTEGER mess, count, exevrefnum, toskip; AppFile thefile;
    Byte *p;
    int i;
    WDPBRec wdpb;
    CInfoPBRec hpb;
    Str255 name;
    StringPtr fName;

    SCSIFlags = CWC (0xEC00); /* scsi+clock+xparam+mmu+adb
				 (no fpu,aux or pwrmgr) */

    MCLKPCmiss1 = 0;	/* &MCLKPCmiss1 = 0x358 + 72 (MacLinkPC starts
			   adding the 72 byte offset to VCB pointers too
			   soon, starting with 0x358, which is not the
			   address of a VCB) */
			   
    MCLKPCmiss2 = 0;	/* &MCLKPCmiss1 = 0x358 + 78 (MacLinkPC misses) */
    AuxCtlHead = 0;
    CurDeactive = 0;
    CurActivate = 0;
    macfpstate[0] = 0;
    fondid = 0;
    PrintErr = 0;
    mouseoffset = 0;
    heapcheck = 0;
    DefltStack = CLC(0x2000);	/* nobody really cares about these two */
    MinStack = CLC(0x400);		/* values ... */
    IAZNotify = 0;
    CurPitch = 0;
    JSwapFont = (ProcPtr) RM(P_FMSwapFont);
    JInitCrsr = (ProcPtr) RM(P_InitCursor);

    Key1Trans = (Ptr) RM(P_Key1Trans);
    Key2Trans = (Ptr) RM(P_Key2Trans);
    JFLUSH = (ProcPtr) RM(P_flushcache);
    JResUnknown1 = JFLUSH;	/* I don't know what these are supposed to */
    JResUnknown2 = JFLUSH;	/* do, but they're not called enough for
				   us to worry about the cache flushing
				   overhead */

#if !defined (NEXT) && !defined (SYN68K)
    CPUFlag = 2;	/* mc68020 */
#else /* NEXT || SYN68K */
    CPUFlag = 4;	/* mc68040 */
#endif /* NEXT || SYN68K */
    UnitNtryCnt = 0;	/* how many units in the table */

    TheZone = SysZone;
    VIA = RM(NewPtr(16 * 512));	/* IMIII-43 */
    memset(MR(VIA), 0, (LONGINT) 16 * 512);
    *(char *)MR(VIA) = 0x80;	/* Sound Off */

#define SCC_SIZE 1024

    SCCRd_H.p = RM(NewPtrSysClear (SCC_SIZE));
    SCCWr_H.p = RM(NewPtrSysClear (SCC_SIZE));
    
    setup28();

    SoundBase = RM(NewPtr(370 * sizeof(INTEGER)));
#if 0
    memset(CL(SoundBase), 0, (LONGINT) 370 * sizeof(INTEGER));
#else /* !0 */
    for (i = 0; i < 370; ++i)
	((INTEGER *) MR(SoundBase))[i] = CWC (0x8000);	/* reference 0 sound */
#endif /* !0 */
    TheZone = ApplZone;
    HiliteMode = CB(0xFF);
    /* Mac II has 0x3FFF here */
    ROM85 = CWC(0x3FFF);

    a5 = US_TO_SYN68K((LONGINT) &tmpA5);
    CurrentA5 = (Ptr) CL(a5);
    InitGraf((Ptr) quickbytes + sizeof(quickbytes) - 4);
    InitFonts();
    InitCRM ();
    FlushEvents( everyEvent, 0 );
    InitWindows();
    TEInit();
    InitDialogs((ProcPtr)0);
    InitCursor();

    loadtrap = 0;

#if defined (MSDOS)
    ROMlib_WriteWhen(WriteInBltrgn);
#endif /* MSDOS */

    /* ROMlib_WriteWhen(WriteInOSEvent); */

    FinderName[0] = MIN(strlen(BROWSER_NAME), sizeof(FinderName)-1);
    memcpy(FinderName+1, BROWSER_NAME, FinderName[0]);
    
    CountAppFiles(&mess, &count);
    count = CW (count);
    if (count > 0)
	GetAppFiles(1, &thefile);
    else
	thefile.fType = 0;

#if defined (DISPLAY_SPLASH_SCREEN)  
    /* Reset the CLUT since it may have gotten changed by the splash screen. */
    gd_set_bpp (MR (MainDevice), !vdriver_grayscale_p, vdriver_fixed_clut_p,
		vdriver_bpp);
#endif /* DISPLAY_SPLASH_SCREEN */

#if defined (SYN68K)
#define ALINETRAPNUMBER       0xA
    trap_install_handler( ALINETRAPNUMBER, alinehandler, (void *) 0);
#endif
    dolicense();
    if (thefile.fType == CLC(T('A','P','P','L')))
      {
	ClrAppFiles(1);
	Munger(MR(AppParmHandle), 2L*sizeof(INTEGER), (Ptr) 0,
	       (LONGINT) sizeof(AppFile), (Ptr) "", 0L);
      }
    hpb.hFileInfo.ioNamePtr   = RM(&thefile.fName[0]);
    hpb.hFileInfo.ioVRefNum   = thefile.vRefNum;
    hpb.hFileInfo.ioFDirIndex = CWC (0);
    hpb.hFileInfo.ioDirID     = CLC (0);
    PBGetCatInfo(&hpb, FALSE);

    if (thefile.fType == CLC(T('A','P','P','L')))
      fName = thefile.fName;
    else
      {
	const char *p;

	if (count > 0)
	  {
	    p = ROMlib_find_best_creator_type_match
	      (CL (hpb.hFileInfo.ioFlFndrInfo.fdCreator),
	       CL (hpb.hFileInfo.ioFlFndrInfo.fdType));
	  }
	else
	  {
	    if (!ROMlib_default_appp)
	      p = NULL;
	    else
	      p = (char *) ROMlib_default_appp->chars;
	  }
	fName = name;
	if (!p)
	  ExitToShell ();
	else
	  {
	    ROMlib_exit = TRUE;
	    str255_from_c_string (fName, p);
	    hpb.hFileInfo.ioNamePtr   = RM(fName);
	    hpb.hFileInfo.ioVRefNum   = CWC (0);
	    hpb.hFileInfo.ioFDirIndex = CWC (0);
	    hpb.hFileInfo.ioDirID     = CLC (0);
	    PBGetCatInfo(&hpb, FALSE);

	    {
	      HParamBlockRec hp;
	      Str255 fName2;

	      memset (&hp, 0, sizeof hp);
	      str255assign (fName2, fName);
	      hp.ioParam.ioNamePtr = RM ((StringPtr) fName2);
	      hp.volumeParam.ioVolIndex = CWC (-1);
	      PBHGetVInfo (&hp, FALSE);
	      hpb.hFileInfo.ioVRefNum   = hp.ioParam.ioVRefNum;
	    }
	  }
      }

    for (p = fName + fName[0] + 1;
	 p > fName && *--p != ':';)
      ;
    toskip = p - fName;
    CurApName[0] = MIN(fName[0]-toskip, 31);
    BlockMove((Ptr) fName+1+toskip, (Ptr) CurApName+1,
	      (Size) CurApName[0]);

    wdpb.ioVRefNum = hpb.hFileInfo.ioVRefNum;
    wdpb.ioWDDirID = hpb.hFileInfo.ioFlParID;
    SFSaveDisk_Update (CW (hpb.hFileInfo.ioVRefNum), fName);
    CurDirStore = hpb.hFileInfo.ioFlParID;
    wdpb.ioWDProcID = CLC (T('X','c','t','r'));
    wdpb.ioNamePtr = 0;
    PBOpenWD(&wdpb, FALSE);
    exevrefnum = CW(wdpb.ioVRefNum);
    Launch (CurApName, exevrefnum);  
}
