//
// basic
// Basic bytecode set
// F.J. Alberti
//


case kNop:
  // Idle for an iteration
  break;


case kDrop:
  // Pop stack top
#if defined(SCOL_DEBUG)
  checkUnderflow(m)
#endif
  m->pp++;
  break;


case kDup: {
  // Duplicate stack top
  int val = m->top[m->pp];
  m->top[--m->pp] = val;
  checkOverflow(m, 0)
  break;
}


case kPushnil:
  // Push constant nil
  m->top[--m->pp] = NIL;
  checkOverflow(m, 0)
  break;


case kGoto:
  // Unconditional branch
  pc = *(uint32*)&fetch(pc);
  break;


case kIftrue:
  // Branch if stack top is not 0
#if defined(SCOL_DEBUG)
  checkUnderflow(m)
#endif
  if (m->top[m->pp++] != 0)
	pc = *(uint32*)&fetch(pc);
  else
	pc += ALIGNMENT(4);
  break;


case kIffalse:
  // Branch if stack top is 0
#if defined(SCOL_DEBUG)
  checkUnderflow(m)
#endif
  if (m->top[m->pp++] == 0)
	pc = *(uint32*)&fetch(pc);
  else
	pc += ALIGNMENT(4);
  break;


case kExeccall: {  // to be deprecated
  // Call a function ('exec' calling convention)
  if (m->top[m->pp] == NIL || m->top[m->pp+1] == NIL) {
    m->top[++m->pp] = NIL;
	break;
  }
  int nargs = m->tape[m->top[m->pp]>>1]>>1;
  if (nargs > 0) {
    checkOverflow(m, nargs-1)
    int i;
    int32 pp = m->pp+1;
	// Push nargs-1 arguments initialised to nil
    for (i = 1; i < nargs; i++)
      m->top[--m->pp] = NIL;
    int32 fun  = m->top[pp];      // function
    int32 args = m->top[pp-1]>>1; // arguments tuple
    for (i = 0; i < nargs; i++)
      m->top[pp-i] = m->tape[args+SizeHeader+i];
    m->top[m->pp] = fun;
  } else
    m->pp++;
// fall through
}


case kCall: {
  // Call a function (either native or SCOL)
  tailcall = false;
call:

  // If function pointer is undefined, return an error
  int fun = m->top[m->pp];
  if (fun == NIL)
    throw Exception(MERREP);

/*
  // Print package and function name
  int t = m->tape[(fun>>1)+SizeHeader+OFFPVAR]>>1;
  MMechostr(MSKRUNTIME, "> call '%s#%s'\n",
    (char*)&m->tape[(m->tape[(m->tape[t+SizeHeader+OFFVPKG]>>1)+SizeHeader+OFFPKNAME]>>1)+SizeHeader],
	(char*)&m->tape[(m->tape[t+SizeHeader+OFFVNAME]>>1)+SizeHeader]);
*/
  int s      = (fun>>1)+SizeHeader;
  int nargs  = m->tape[s+OFFPARG]>>1; // function arity
  int nlocs  = m->tape[s+OFFPLOC]>>1; // # of local variables (excluding args)
  int optarg = m->tape[s+OFFPOPT];    // pointer to first optional arg, if any

#if defined(INCLUDE_DEBUGGER)
  if (debug.sock) {
//$ FA(04/09/2001): Update current source position
    //DBGUpdatePosition(m, (pc == 0) ? 0 : pc-ALIGNMENT(1));
//$ FA(18/11/2000): Break if trace flag has been set
	if (m->tape[s+OFFPDBG] && debug.traceMode != kTraceNone) {
      MMechostr(MSKTRACE, "User break at function (%x)\n", fun);
      DBGRequestBreak(m, kBrksrcBreakpoint);
	}
  }
#endif

  // Handle functions generated through 'newfun'
  while (optarg != NIL) {
    m->top[--m->pp] = optarg;
	checkOverflow(m, 1);
    optarg = m->top[m->pp++];
    m->top[--m->pp] = m->tape[(optarg>>1)+SizeHeader];
    // Swap
    int val         = m->top[m->pp]; 
    m->top[m->pp]   = m->top[m->pp+1]; 
    m->top[m->pp+1] = val;
    optarg = m->tape[(optarg>>1)+SizeHeader+1];
    nargs++;
  } // while

  // Call a native function
  if (nlocs == NIL) {  // nlocs is not a SCOL int here!
    int fun = m->top[m->pp];
    Native native = (Native)m->tape[(m->tape[(fun>>1)+SizeHeader+OFFPBCD]>>1)+SizeHeader];
	m->pp++;  // drop function pointer
#if defined(SCOL_DEBUG) || defined(RELEASE_DEVELOPER)
	int pp = m->pp;
#endif
    if (native) {
	  if (res = (*native)(m))
	    throw Exception(res);
	} else {
#if defined(RELEASE_DEVELOPER)
      // This case never takes place given the actual definition
	  // of native package linking
      int s = m->tape[(fun>>1)+SizeHeader+OFFPVAR]>>1;
      MMechostr(MSKRUNTIME, "(!) "MSGWNATIVEISNULL"\n",
	    (char*)&m->tape[(m->tape[(m->tape[s+SizeHeader+OFFVPKG]>>1)+SizeHeader+OFFPKNAME]>>1)+SizeHeader],
	    (char*)&m->tape[(m->tape[s+SizeHeader+OFFVNAME]>>1)+SizeHeader]);
#endif
	  m->pp += nargs;
	  m->top[--m->pp] = NIL;
	  checkOverflow(m, 0)
	  break;
	}
    // Terminate Interpreter::exec() if returning from first call
    if (fp == 0)
	  return MERREND;
#if defined(SCOL_DEBUG) || defined(RELEASE_DEVELOPER)
	if (m->pp != pp+nargs-1) {
# if defined(RELEASE_DEVELOPER)
      int s = m->tape[(fun>>1)+SizeHeader+OFFPVAR]>>1;
      MMechostr(MSKRUNTIME, "(!) "MSGENATIVEMISBEHAVED"\n",
	    (char*)&m->tape[(m->tape[(m->tape[s+SizeHeader+OFFVPKG]>>1)+SizeHeader+OFFPKNAME]>>1)+SizeHeader],
	    (char*)&m->tape[(m->tape[s+SizeHeader+OFFVNAME]>>1)+SizeHeader]);
# else
      MMechostr(MSKRUNTIME, "(!) "MSGENATIVEMISBEHAVED"\n", native, nargs);
# endif
      // Dump register values
	  MMechostr(MSKRUNTIME, "  sp = %x (expected at %x)\n", m->pp, pp+nargs-1);
	  MMechostr(MSKRUNTIME, "  fp = %x\n", fp);
	  MMechostr(MSKRUNTIME, "  bp = %x\n", bp);
	  MMechostr(MSKRUNTIME, "  pc = %x\n", pc);
	  throw Exception(MERRRET);
	}
#endif
    break;
  }

#if !defined(RELEASE_DEVELOPER)
  if (tailcall) {
    int diff = fp-bp-5-nargs-nlocs;  // locals area size difference
    int opc;                         // old program counter
    int obp;                         // old base pointer of operand stack
    int ofp;                         // old frame pointer
    // If there is not enough space in the locals area, save the values of
    // the caller's registers to reconstruct frame below
    if (diff < 0) {
      opc = m->top[bp];
      obp = m->top[bp+1];
      ofp = m->top[bp+2];
      checkOverflow(m, -diff);
    }
    fun = m->top[m->pp];
    // Overwrite caller's arguments. (Rather expensive; but effective.)
    int ap = m->pp+nargs;
    m->pp = fp+1;
	int i;
    for (i = 0; i < nargs; i++)
      m->top[--m->pp] = m->top[ap--];
    // Initialise local variables
    for (i = 0; i < nlocs; i++)
      m->top[--m->pp] = NIL;

    if (diff < 0) {
      m->top[--m->pp] = fun;      // pointer to function
      m->top[--m->pp] = m->tape[(fun>>1)+SizeHeader+OFFPBCD]; // pointer to code
      m->top[--m->pp] = m->tape[(fun>>1)+SizeHeader+OFFPREF]; // pointer to external references
      m->top[--m->pp] = ofp;      // save caller's frame pointer
      m->top[--m->pp] = obp;      // save caller's base pointer of operand stack
      m->top[--m->pp] = opc;      // save caller's program counter  
      bp = m->pp;          
    } else {
      m->top[bp+5] = fun;         // pointer to function
      m->top[bp+4] = m->tape[(fun>>1)+SizeHeader+OFFPBCD];    // pointer to code
      m->top[bp+3] = m->tape[(fun>>1)+SizeHeader+OFFPREF];    // pointer to external references
      m->pp = bp;                 // stack pointer points to base of operand stack
    }
  } else {
#endif
    // Ensure that there is enough stack space
    checkOverflow(m, nlocs+FRAMESIZE);
    fun = m->top[m->pp];
    int pp = (++m->pp)+nargs-1;    // index of (supposed) first argument

    // Setup the new frame
    // Initialise local variables
    for (int i = 0; i < nlocs; i++)
      m->top[--m->pp] = NIL;

#if defined(RELEASE_DEVELOPER)
    m->top[--m->pp] = NIL;     // current list of local variables
    m->top[--m->pp] = NIL;     // current source position
#endif
    m->top[--m->pp] = fun;     // pointer to function
    m->top[--m->pp] = m->tape[(fun>>1)+SizeHeader+OFFPBCD];    // pointer to code
    m->top[--m->pp] = m->tape[(fun>>1)+SizeHeader+OFFPREF];    // pointer to external references
    m->top[--m->pp] = fp<<1;   // save current frame pointer
    m->top[--m->pp] = bp<<1;   // save current base pointer of operand stack
    m->top[--m->pp] = pc<<1;   // save current program counter
  
    bp = m->pp;
    fp = pp;
#if !defined(RELEASE_DEVELOPER)
  }
#endif
  pc = 0;
  break;
}


case kTailcall:
  tailcall = true;
  goto call;


case kCallnative:
  // Reserved for calling a native function
  break;


case kReturn: {
  // Return from a SCOL function
#if defined(SCOL_DEBUG) || defined(RELEASE_DEVELOPER)
  if (m->pp != bp-1) {
	// Badly behaved function call
    MMechostr(MSKRUNTIME, "(!) "MSGIFUNRET"\n");
	MMechostr(MSKRUNTIME, "  sp = %x (expected at %x)\n", m->pp, bp-1);
	throw Exception(MERRRET);
  }
#endif
  // Copy result
  int pp     = fp;
  m->top[pp] = m->top[m->pp++];
  // Restore caller's state
  pc = m->top[m->pp++]>>1;
  bp = m->top[m->pp++]>>1;
  fp = m->top[m->pp++]>>1;
  m->pp = pp;
  // Stop Interpreter::exec() if returning from first call
  if (fp == 0) 
    return MERREND;
  break;
}


case kNewfun: {
  // Revise and rewrite
  int32 fun = m->top[m->pp+1];
  if (fun == NIL) {
    m->pp++;
	break;
  }
  checkOverflow(m, 2)
  m->top[--m->pp] = m->tape[(fun>>1)+SizeHeader+OFFPOPT];
  m->top[--m->pp] = 2<<1;
  if (res = MBdeftab(m))
    throw Exception(res);

  int s = MMmalloc(m, SIZEPROG, TYPETAB); 
  if (s == NIL)
    throw Exception(MERRMEM);

  fun = m->top[m->pp+1]>>1;
  m->tape[s+SizeHeader+OFFPARG] = m->tape[fun+SizeHeader+OFFPARG]-(1<<1);
  m->tape[s+SizeHeader+OFFPLOC] = m->tape[fun+SizeHeader+OFFPLOC];
  m->tape[s+SizeHeader+OFFPBCD] = m->tape[fun+SizeHeader+OFFPBCD];
  m->tape[s+SizeHeader+OFFPREF] = m->tape[fun+SizeHeader+OFFPREF];
  m->tape[s+SizeHeader+OFFPOPT] = m->top[m->pp++];
#if defined(RELEASE_DEVELOPER)
//$ FA(07/11/2000)
  m->tape[s+SizeHeader+OFFPVAR] = m->tape[fun+SizeHeader+OFFPVAR];
//$ FA(18/11/2000)
  // debugging options are inherited
  m->tape[s+SizeHeader+OFFPDBG] = m->tape[fun+SizeHeader+OFFPDBG];
//$ FA(23/11/2000)
  m->tape[s+SizeHeader+OFFPBPT] = m->tape[fun+SizeHeader+OFFPBPT];
//
#endif
  m->top[m->pp] = s<<1|0x00000001;
  break;
}


case kPushstr: {
  // Push a string constant
  uint32 len = *(uint32*)&fetch(pc);             // length of origin
  pc += ALIGNMENT(4);

  int s = MMmalloc(m, ((len+4)>>2)+1, TYPEBUF);
  if (s == NIL)
    throw Exception(MERRMEM);

  m->tape[s+SizeHeader] = len;
  char* org = (char*)&fetch(pc);                // origin
  pc += len*ALIGNMENT(1);
  char* dst = (char*)&m->tape[s+SizeHeader+1];  // destination
  for (uint32 i = 0; i < len; i++) {
	*dst++ = *org;
	org += ALIGNMENT(1);
  }
  *dst = '\0';

  m->top[--m->pp] = (s<<1)|0x00000001;
  checkOverflow(m, 0)
  break;
}