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

#include <string.h>
#include <assert.h>

#include "ztypes.h"
#include "memory.h"
#include "operand.h"
#include "control.h"

#if 0

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

unsigned load_operand(int type)
{
    unsigned operand;

    if (type)
    {
        /* Type 1: byte literal, or type 2: operand specifier */

        operand = read_code_byte ();

        if (type != 2)
            return operand;

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

        if (operand)
            return load_variable(operand);
        else
            return *sp++;
    }
    else

        /* Type 0: word literal */

        return read_code_word ();

    /*assert(operand < 0x10000);*/

    return operand;

}/* load_operand */


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

void store_operand(zword_t operand)
{
    zbyte_t specifier;

    /* Read operand specifier byte */

    specifier = read_code_byte();

    z_store(specifier, operand);

}/* store_operand */


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

unsigned load_variable(int number)
{
    unsigned variable;

    if (number)
    {
        if (number < 16)

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

            variable = LOCAL_VARIABLE(number);
        else
        {
            /* number > 15, it's a global variable */

            unsigned addr = h_globals_offset + ((number - 16) * 2);

            variable = get_word_priv(addr);
        }
    }
    else
        /* number = 0, get from top of stack */

        variable = *sp;

    return variable;

}/* load_variable */


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

void z_store(int number, unsigned variable)
{
    if (number)
    {
        if (number < 16)

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

            LOCAL_VARIABLE(number) = 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 */

        *sp = variable;

}/* store */


/*
 * 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!
 *
 * Note - our assembler version requires flag to be 0 or 1 only!
 */

void conditional_jump(int flag)
{
    int specifier;
    int 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;

    /* Jump offset is in bottom 6 bits */

    offset = 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)
    {
        /* If offset equals 0 or 1 return that value instead */

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

            /* Add offset to PC */

            pc = pc + (short) offset - 2;
    }

}/* conditional_jump */

#endif
