/*
 * operand.c
 *
 * Operand manipulation routines
 *
 */

#include "ztypes.h"

/*
 * load_operand
 *
 * Load an operand, either: a variable, popped from the stack or a literal.
 *
 */

#ifdef __STDC__
zword_t load_operand (int type)
#else
zword_t load_operand (type)
int type;
#endif
{
    zword_t operand;

    if (type) {

        /* Type 1: byte literal, or type 2: operand specifier */

        operand = (zword_t) read_code_byte ();
        if (type == 2) {

            /* If operand specifier non-zero then it's a variable, otherwise
               it's the top of the stack */

            if (operand)
                operand = load_variable (operand);
            else
                operand = stack[sp++];
        }
    } else

        /* Type 0: word literal */

        operand = read_code_word ();

    return (operand);

}/* load_operand */

/*
 * store_operand
 *
 * Store an operand, either as a variable pushed on the stack.
 *
 */

#ifdef __STDC__
void store_operand (zword_t operand)
#else
void store_operand (operand)
zword_t operand;
#endif
{
    zbyte_t specifier;

    /* Read operand specifier byte */

    specifier = read_code_byte ();

    /* If operand specifier non-zero then it's a variable, otherwise it's the
       pushed on the stack */

    if (specifier)
        store_variable (specifier, operand);
    else
        stack[--sp] = operand;

}/* store_operand */

/*
 * load_variable
 *
 * Load a variable, either: a stack local variable, a global variable or the top
 * of the stack.
 *
 */

#ifdef __STDC__
zword_t load_variable (int number)
#else
zword_t load_variable (number)
int number;
#endif
{
    zword_t variable;

    if (number) {
        if (number < 16)

            /* number in range 1 - 15, it's a stack local variable */

            variable = stack[fp - (number - 1)];
        else

            /* number > 15, it's a global variable */

            variable = get_word (h_globals_offset + ((number - 16) * 2));
    } else

        /* number = 0, get from top of stack */

        variable = stack[sp];

    return (variable);

}/* load_variable */

/*
 * store_variable
 *
 * Store a variable, either: a stack local variable, a global variable or the top
 * of the stack.
 *
 */

#ifdef __STDC__
void store_variable (int number, zword_t variable)
#else
void store_variable (number, variable)
int number;
zword_t variable;
#endif
{

    if (number) {
        if (number < 16)

            /* number in range 1 - 15, it's a stack local variable */

            stack[fp - (number - 1)] = variable;
        else

            /* number > 15, it's a global variable */

            set_word (h_globals_offset + ((number - 16) * 2), variable);
    } else

        /* number = 0, get from top of stack */

        stack[sp] = variable;

}/* store_variable */

/*
 * conditional_jump
 *
 * Take a jump after an instruction based on the flag, either true or false. The
 * jump can be modified by the change logic flag. Normally jumps are taken
 * when the flag is true. When the change logic flag is set then the jump is
 * taken when flag is false. A PC relative jump can also be taken. This jump can
 * either be a positive or negative byte or word range jump. An additional
 * feature is the return option. If the jump offset is zero or one then that
 * literal value is passed to the return instruction, instead of a jump being
 * taken. Complicated or what!
 *
 */

#ifdef __STDC__
void conditional_jump (int flag)
#else
void conditional_jump (flag)
int flag;
#endif
{
    zbyte_t specifier;
    zword_t offset;

    /* Read the specifier byte */

    specifier = read_code_byte ();

    /* If the reverse logic flag is set then reverse the flag */

    if (specifier & 0x80)
        flag = (flag) ? 0 : 1;

    /* Jump offset is in bottom 6 bits */

    offset = (zword_t) specifier & 0x3f;

    /* If the byte range jump flag is not set then load another offset byte */

    if ((specifier & 0x40) == 0) {

        /* Add extra offset byte to existing shifted offset */

        offset = (offset << 8) + read_code_byte ();

        /* If top bit of offset is set then propogate the sign bit */

        if (offset & 0x2000)
            offset |= 0xc000;
    }

    /* If the flag is false then do the jump */

    if (flag == 0)

        /* If offset equals 0 or 1 return that value instead */

        if (offset == 0 || offset == 1)
            ret (offset);
        else

            /* Add offset to PC */

            pc = (unsigned long) (pc + (short) offset - 2);

}/* conditional_jump */
