/*
 * interpre.c
 *
 * Main interpreter loop
 *
 */

#include <stdlib.h>
#include <stdio.h>

#include "ztypes.h"
#include "interpre.h"
#include "memory.h"
#include "math.h"
#include "fileio.h"
#include "variable.h"
#include "property.h"
#include "text.h"
#include "object.h"
#include "operand.h"
#include "screen.h"
#include "control.h"
#include "osdepend.h"
#include "input.h"
#include "v6.h"

/*
 * interpret
 *
 * Interpret Z code
 *
 */

void interpret(void)
{
    unsigned opcode;
    unsigned specifier, operand[8];
    int maxoperands, count, extended, i;
    int pollcount=8192;

    extended = 0;

    /* Loop until HALT instruction executed, or an interrupt routine returns */

    while (!interrupt_finished)
    {
        if (--pollcount==0)
        {
            pollcount=8192;
            gasp();
        }
        /* Load opcode and set operand count */

        opcode = read_code_byte();

        count = 0;

        if (opcode < 128)
        {
            unsigned op1, op2;
            op1 = load_operand ((opcode & 0x40) ? 2 : 1);
            op2 = load_operand ((opcode & 0x20) ? 2 : 1);
            opcode &= 0x1f;
            switch (opcode)
            {
                /* Two or multiple operand instructions */

                case 0x01: conditional_jump(op1 == op2); break;
                case 0x02: z_jl(op1, op2); break;
                case 0x03: z_jg(op1, op2); break;
                case 0x04: z_dec_chk(op1, op2); break;
                case 0x05: z_inc_chk(op1, op2); break;
                case 0x06: z_jin(op1, op2); break;
                case 0x07: conditional_jump((op2 &~ op1) == 0); break;
                case 0x08: store_operand(op1 | op2); break;
                case 0x09: store_operand(op1 & op2); break;
                case 0x0a: z_test_attr(op1, op2); break;
                case 0x0b: z_set_attr(op1, op2); break;
                case 0x0c: z_clear_attr(op1, op2); break;
                case 0x0d: z_store(op1, op2); break;
                case 0x0e: z_insert_obj(op1, op2); break;
                case 0x0f: z_loadw(op1, op2); break;
                case 0x10: z_loadb(op1, op2); break;
                case 0x11: z_get_prop(op1, op2); break;
                case 0x12: z_get_prop_addr(op1, op2); break;
                case 0x13: z_get_next_prop(op1, op2); break;
                case 0x14: store_operand(op1 + op2); break;
                case 0x15: store_operand(op1 - op2); break;
                case 0x16: store_operand(op1 * op2); break;
                case 0x17: z_div(op1, op2); break;
                case 0x18: z_mod(op1, op2); break;
                case 0x19: operand[0] = op1;
                           operand[1] = op2;
                           z_call(2, operand, FUNCTION);
                           break;
                case 0x1a: operand[0] = op1;
                           operand[1] = op2;
                           z_call(2, operand, PROCEDURE);
                           break;
                case 0x1b: operand[0] = op1;
                           operand[1] = op2;
                           z_set_colour(2, operand);
                           break;
                case 0x1c: z_throw(op1, op2); break;
                case 0x1d: z_xor(op1, op2); break;
                default: fatal("Illegal operation");
            }
        }
        else if (opcode < 176)
        {
            unsigned op = load_operand((opcode >> 4) & 0x03);
            switch (opcode & 0x0f)
            {
                case 0x00: conditional_jump(op == 0); break;
                case 0x01: z_get_sibling(op); break;
                case 0x02: z_get_child(op); break;
                case 0x03: z_get_parent(op); break;
                case 0x04: z_get_prop_len(op); break;
                case 0x05: z_inc(op); break;
                case 0x06: z_dec(op); break;
                case 0x07: z_print_addr(op); break;
                case 0x08: z_call(1, &op, FUNCTION); break;
                case 0x09: z_remove_obj(op); break;
                case 0x0a: z_print_obj(op); break;
                case 0x0b: z_ret(op); break;
                case 0x0c: z_jump(op); break;
                case 0x0d: z_print_paddr(op); break;
                case 0x0e: z_load(op); break;
                case 0x0f:
                    if (h_type > V4)
                        z_call(1, &op, PROCEDURE);
                    else
                        z_not(op);
                    break;
            }
        }
        else if (opcode < 192)
        {
            /* Zero operand class, execute instruction */

            switch (opcode & 0x0f)
            {
                case 0x00: z_ret(TRUE); break;         /* rtrue */
                case 0x01: z_ret(FALSE); break;        /* rfalse */
                case 0x02: z_print(); break;
                case 0x03: z_print_ret(); break;
                case 0x04: break;                      /* nop */
                case 0x05: z_save(0, NULL); break;
                case 0x06: z_restore(0, NULL); break;
                case 0x07: z_restart(); break;
                case 0x08: z_ret(*sp++); break;  /* ret_popped */
                case 0x09: z_catch(); break;
                case 0x0a: return;                    /* quit */
                case 0x0b: z_new_line(); break;
                case 0x0c: z_show_status(); break;
                case 0x0d: z_verify(); break;
                case 0x0e: opcode = read_code_byte();
                           goto handle_extended;
                case 0x0f: z_piracy(); break;
            }
        }
        else
        {
            /* Variable operand class, load operand specifier */

            if (opcode == 236 || opcode == 250) /* Extended CALL instruction */
            {
                specifier = read_code_word();
                maxoperands = 8;
            }
            else
            {
              handle_extended:
                specifier = read_code_byte();
                maxoperands = 4;
            }

            /* Load operands */

            for (i = (maxoperands - 1) * 2; i >= 0; i -= 2)
                if (((specifier >> i) & 0x03) != 3)
                    operand[count++] = load_operand ((specifier >> i) & 0x03);
                else
                    i = 0;

            switch (opcode)
            {
                /* Two or multiple operand instructions */

                case 0xc0:
                case 0xde:
                case 0xdf: fatal("Illegal operation");
                case 0xc1: z_je(count, operand); break;
                case 0xc2: z_jl(operand[0], operand[1]); break;
                case 0xc3: z_jg(operand[0], operand[1]); break;
                case 0xc4: z_dec_chk(operand[0], operand[1]); break;
                case 0xc5: z_inc_chk(operand[0], operand[1]); break;
                case 0xc6: z_jin(operand[0], operand[1]); break;
                case 0xc7: z_test(operand[0], operand[1]); break;
                case 0xc8: z_or(operand[0], operand[1]); break;
                case 0xc9: z_and(operand[0], operand[1]); break;
                case 0xca: z_test_attr(operand[0], operand[1]); break;
                case 0xcb: z_set_attr(operand[0], operand[1]); break;
                case 0xcc: z_clear_attr(operand[0], operand[1]); break;
                case 0xcd: z_store(operand[0], operand[1]); break;
                case 0xce: z_insert_obj(operand[0], operand[1]); break;
                case 0xcf: z_loadw(operand[0], operand[1]); break;
                case 0xd0: z_loadb(operand[0], operand[1]); break;
                case 0xd1: z_get_prop(operand[0], operand[1]); break;
                case 0xd2: z_get_prop_addr(operand[0], operand[1]); break;
                case 0xd3: z_get_next_prop(operand[0], operand[1]); break;
                case 0xd4: z_add(operand[0], operand[1]); break;
                case 0xd5: z_sub(operand[0], operand[1]); break;
                case 0xd6: z_mul(operand[0], operand[1]); break;
                case 0xd7: z_div(operand[0], operand[1]); break;
                case 0xd8: z_mod(operand[0], operand[1]); break;
                case 0xd9: z_call(count, operand, FUNCTION); break;
                case 0xda: z_call(count, operand, PROCEDURE); break;
                case 0xdb: z_set_colour(count, operand); break;
                case 0xdc: z_throw(operand[0], operand[1]); break;

                /* Variable-operand instructions */

                case 0xe0: z_call(count, operand, FUNCTION); break;
                case 0xe1: z_storew(operand[0], operand[1], operand[2]); break;
                case 0xe2: z_storeb(operand[0], operand[1], operand[2]); break;
                case 0xe3: z_put_prop(operand[0], operand[1], operand[2]); break;
                case 0xe4: z_read(count, operand); break;
                case 0xe5: z_print_char(operand[0]); break;
                case 0xe6: z_print_num(operand[0]); break;
                case 0xe7: z_random(operand[0]); break;
                case 0xe8: z_push(operand[0]); break;
                case 0xe9: z_pull(count, operand); break;
                case 0xea: z_split_window(operand[0]); break;
                case 0xeb: z_set_window(operand[0]); break;
                case 0xec: z_call(count, operand, FUNCTION); break;
                case 0xed: z_erase_window(operand[0]); break;
                case 0xee: z_erase_line(operand[0]); break;
                case 0xef: z_set_cursor(count, operand); break;
                case 0xf0: z_get_cursor(operand[0]); break;
                case 0xf1: z_set_text_style(operand[0]); break;
                case 0xf2: z_buffer_mode(operand[0]); break;
                case 0xf3: z_output_stream(count, operand[0], operand[1], operand[2]); break;
                case 0xf4: z_input_stream(operand[0]); break;
                case 0xf5: z_sound_effect(count, operand); break;
                case 0xf6: z_read_char(count, operand); break;
                case 0xf7: z_scan_table(count, operand); break;
                case 0xf8: z_not(operand[0]); break;
                case 0xf9: z_call (count, operand, PROCEDURE); break;
                case 0xfa: z_call (count, operand, PROCEDURE); break;
                case 0xfb: z_tokenise(count, operand); break;
                case 0xfc: z_encode_text(operand[0], operand[1], operand[2], operand[3]); break;
                case 0xfd: z_copy_table(operand[0], operand[1], operand[2]); break;
                case 0xfe: z_print_table(count, operand); break;
                case 0xff: z_check_arg_count(operand[0]); break;

                /* Extended operand instructions */

                case 0x100: z_save(count, operand); break;
                case 0x101: z_restore(count, operand); break;
                case 0x102: z_log_shift(operand[0], operand[1]); break;
                case 0x103: z_art_shift(operand[0], operand[1]); break;
                case 0x104: z_set_font(count, operand); break;
                case 0x105: z_draw_picture(count, operand); break;
                case 0x106: z_picture_data(operand[0], operand[1]); break;
                case 0x107: z_erase_picture(count, operand); break;
                case 0x108: z_set_margins(count, operand); break;
                case 0x109: z_save_undo(); break;
                case 0x10a: z_restore_undo(); break;
                case 0x10b: z_print_unicode(operand[0]); break;
                case 0x10c: z_check_unicode(operand[0]); break;
                case 0x10d: z_set_true_colour(count, operand); break;
                case 0x10e:
#ifdef ZSPEC11DRAFT
                            z_sound_data(operand[0], operand[1]); break;
#endif
                case 0x10f: fatal ("Illegal operation");
                case 0x110: z_move_window(operand[0], operand[1], operand[2]); break;
                case 0x111: z_window_size(operand[0], operand[1], operand[2]); break;
                case 0x112: z_window_style(count, operand); break;
                case 0x113: z_get_wind_prop(operand[0], operand[1]); break;
                case 0x114: z_scroll_window(operand[0], operand[1]); break;
                case 0x115: z_pop_stack(count, operand); break;
                case 0x116: z_read_mouse(operand[0]); break;
                case 0x117: z_mouse_window(operand[0]); break;
                case 0x118: z_push_stack(count, operand); break;
                case 0x119: z_put_wind_prop(operand[0], operand[1], operand[2]); break;
                case 0x11a: z_print_form(operand[0]); break;
                case 0x11b: z_make_menu(operand[0], operand[1]); break;
                case 0x11c: z_picture_table(operand[0]); break;
                case 0x11d: z_buffer_screen(operand[0]); break;
                /* Standard says not to fault EXT:30 to EXT:255 */
            }
        }
    }

}/* interpret */
