/*
** stack.ops
*/

VERSION = PARROT_VERSION;

=head1 NAME

stack.ops - Stack Ops

=head1 DESCRIPTION

Operations that manipulate Parrot's register and user stacks.

=cut

###############################################################################

=head2 Register stack operations

These operations affect entire sets of registers.

=over 4

=cut

########################################

=item B<saveall>()

Save all of the registers onto the register stack.

=item B<restoreall>()

Restore all the registers from the register stack.

=cut

inline op saveall() :base_core {
  Parrot_push_regs(interp);
  /* these 2 opcodes change the register base pointer
   * restart NEXT() reloads cached base pointers, and works with
   * arbitrary branch opcodes too. While it's a bit of overkill,
   * we don't have an opcode annotation to reload just the base pointers
   * TODO OPTIMIZE later
   */
  restart NEXT();
}

inline op restoreall() :base_core {
  Parrot_pop_regs(interp);
  restart NEXT();
}

=back

=cut

###############################################################################

=head2 User stack operations

These operations affect individual registers.

=over 4

=cut


########################################

=item B<entrytype>(out INT, in INT)

Gets the type of entry $2 of the user stack and puts it in $1.

=cut

op entrytype(out INT, in INT) :base_core {
  Stack_Entry_t * const entry =
    stack_entry(interp, CONTEXT(interp->ctx)->user_stack, $2);

  if (!entry) {
    internal_exception(99, "Stack Depth wrong");
  }
  $1 = get_entry_type(interp, entry);
  goto NEXT();
}

########################################

=item B<depth>(out INT)

Puts the depth of the user stack in $1.

=cut

inline op depth(out INT) :base_core {
  $1 = stack_height(interp, CONTEXT(interp->ctx)->user_stack);
  goto NEXT();
}

#######################################

=item B<lookback>(out INT, in INT)

=item B<lookback>(out STR, in INT)

=item B<lookback>(out NUM, in INT)

=item B<lookback>(out PMC, in INT)

Finds the entry in the user stack at offset $2 from the top and put it
in register $1. Positive numbers count down from the top of the stack,
negative numbers count up from the bottom. (0 is the topmost entry, -1
is the bottom-most)

=cut

op lookback(out INT, in INT) :base_core {
  Stack_Entry_t * const entry =
        stack_entry(interp, CONTEXT(interp->ctx)->user_stack, $2);
  if (!entry)
      internal_exception(99, "Stack depth wrong");
  if (entry->entry_type != STACK_ENTRY_INT) {
    internal_exception(ERROR_BAD_STACK_TYPE, "Wrong type on stack!");
  }

  $1 = UVal_int(entry->entry);
  goto NEXT();
}

op lookback(out STR, in INT) :base_core {
  Stack_Entry_t * const entry =
        stack_entry(interp, CONTEXT(interp->ctx)->user_stack, $2);
  if (!entry)
      internal_exception(99, "Stack depth wrong");
  if (entry->entry_type != STACK_ENTRY_STRING) {
    internal_exception(ERROR_BAD_STACK_TYPE, "Wrong type on stack!");
  }

  $1 = UVal_str(entry->entry);
  goto NEXT();
}

op lookback(out NUM, in INT) :base_core {
  Stack_Entry_t * const entry =
        stack_entry(interp, CONTEXT(interp->ctx)->user_stack, $2);
  if (!entry)
      internal_exception(99, "Stack depth wrong");
  if (entry->entry_type != STACK_ENTRY_FLOAT) {
    internal_exception(ERROR_BAD_STACK_TYPE, "Wrong type on stack!");
  }

  $1 = UVal_num(entry->entry);
  goto NEXT();
}

op lookback(out PMC, in INT) :base_core {
  Stack_Entry_t * const entry =
        stack_entry(interp, CONTEXT(interp->ctx)->user_stack, $2);
  if (!entry)
      internal_exception(99, "Stack depth wrong");
  if (entry->entry_type != STACK_ENTRY_PMC) {
    internal_exception(ERROR_BAD_STACK_TYPE, "Wrong type on stack!");
  }

  $1 = UVal_pmc(entry->entry);
  goto NEXT();
}

########################################

=item B<save>(in INT)

=item B<save>(in NUM)

=item B<save>(in STR)

=item B<save>(invar PMC)

Save register or constant $1 onto the user stack.

=cut

inline op save(in INT) :base_core {
  INTVAL i = $1;
  stack_push(interp,
             &CONTEXT(interp->ctx)->user_stack, &i,
             STACK_ENTRY_INT, STACK_CLEANUP_NULL);
  goto NEXT();
}

inline op save(in NUM) :base_core {
  stack_push(interp, &CONTEXT(interp->ctx)->user_stack,
             &($1), STACK_ENTRY_FLOAT, STACK_CLEANUP_NULL);
  goto NEXT();
}

inline op save(invar PMC) :base_core {
  stack_push(interp, &CONTEXT(interp->ctx)->user_stack,
              $1, STACK_ENTRY_PMC, STACK_CLEANUP_NULL);
  goto NEXT();
}

inline op save(in STR) :base_core {
  stack_push(interp, &CONTEXT(interp->ctx)->user_stack,
             $1, STACK_ENTRY_STRING, STACK_CLEANUP_NULL);
  goto NEXT();
}

########################################

=item B<savec>(in STR)

Save a clone of register or constant $1 onto the user stack.

=cut

inline op savec(in STR) :base_core {
  stack_push(interp, &CONTEXT(interp->ctx)->user_stack,
             string_copy(interp, $1),
             STACK_ENTRY_STRING, STACK_CLEANUP_NULL);
  goto NEXT();
}

########################################

=item B<restore>(out INT)

=item B<restore>(out NUM)

=item B<restore>(out PMC)

=item B<restore>(out STR)

Restore register $1 from the user stack.

=cut

inline op restore(out INT) :base_core {
  (void)stack_pop(interp, &CONTEXT(interp->ctx)->user_stack, &($1),
                  STACK_ENTRY_INT);
  goto NEXT();
}

inline op restore(out NUM) :base_core {
  (void)stack_pop(interp, &CONTEXT(interp->ctx)->user_stack, &($1),
                  STACK_ENTRY_FLOAT);
  goto NEXT();
}

inline op restore(out PMC) :base_core {
  (void)stack_pop(interp, &CONTEXT(interp->ctx)->user_stack, &($1),
                  STACK_ENTRY_PMC);
  goto NEXT();
}

inline op restore(out STR) :base_core {
  (void)stack_pop(interp, &CONTEXT(interp->ctx)->user_stack, &($1),
                  STACK_ENTRY_STRING);
  goto NEXT();
}

########################################

=item B<rotate_up>(in INT)

Rotate the top $1 entries in the user stack by one. If $1 is positive,
then the stack rotates upwards: the ($1)th entry becomes the ($1-1)th
entry and so on, all the way up to the top of the stack, while the
entry formerly at the top of the stack becomes the new ($1)th entry.
If $1 is negative, the stack rotates downwards: the top entry becomes
the second entry, the second becomes the third etc., while the former
($1)th entry becomes the new top entry.

=cut

inline op rotate_up(in INT) :base_core {
  rotate_entries(interp, &CONTEXT(interp->ctx)->user_stack, $1);
  goto NEXT();
}

=back

=cut

###############################################################################

=head1 COPYRIGHT

Copyright (C) 2001-2006, The Perl Foundation.

=head1 LICENSE

This program is free software. It is subject to the same license
as the Parrot interpreter itself.

=cut

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
