
/*
 * IFO VIRTUAL MACHINE
 *
 * Copyright (C) 2000  Thomas Mirlacher
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * The author may be reached as dent@cosy.sbg.ac.at, or
 * Thomas Mirlacher, Jakob-Haringerstr. 2, A-5020 Salzburg,
 * Austria
 *
 *------------------------------------------------------------
 *
 */


#ifdef PARSER
#include <stdio.h>
#endif
#include <sys/types.h>
//#include <unistd.h>
#include "ifo.h"

#include "misc.h"
#include "decode.h"

typedef struct {
	u_char	foo	: 4;
	u_char	cmd	: 4;
	u_char	sub_cmd	: 4;
	u_char	cmp	: 4;
	u_char	val5	: 8;
	u_char	val4	: 8;
	u_char	val3	: 8;
	u_char	val2	: 8;
	u_char	val1	: 8;
	u_char	val0	: 8;
} op_t;	


static void _cmd_nop (u_char *ptr);
static void _cmd_goto (u_char *ptr);
static void _cmd_lnk (u_char *ptr);
static void _cmd_jmp (u_char *ptr);
static void _cmd_setsystem (u_char *ptr);
static void _cmd_setsystem2 (u_char *ptr);
static void _cmd_set (u_char *ptr);
static void _cmd_set2 (u_char *ptr);
static void _cmd_unknown (u_char *ptr);

static struct {
	void (*cmd) (u_char *ptr);
} cmd_switch[] = {
	{_cmd_nop},
	{_cmd_goto},
	{_cmd_lnk},
	{_cmd_jmp},
	{_cmd_setsystem2},
	{_cmd_setsystem},
	{_cmd_set2},
	{_cmd_set},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
	{_cmd_unknown},
};


/**
 * System register
 */

static void _PrintRegister (u_int reg)
{
	char reg_name[][80]={
		"Menu_Language",
		"Audio_Stream_#",
		"SubPicture_Stream_#",
		"Angle_#",
		"Title_#",		// VTS_TTN
		"VTS Title_#",
		"PGC_#",
		"PTT_#",
		"Highlighted_Button_#",
		"Nav_Timer",
		"TimedPGC_#",
		"Audio_Mixing_Mode_for_Karaoke",
		"Country_Code_for_Parental_Mgmt",
		"Parental_Level",
		"Player_Video_Cfg",
		"Player_Audio_Cfg",
		"Initial_Language_for_Audio",
		"Initial_Language_for_SPU",
		"Initial_LanguageExt_for_Audio",
		"Initial_LanguageExt_for_SPU",
		"21", "22", "23", "24" };


//DENT: check if registers are in range
	if (reg & 0x80)
		printf ("r[%s]", reg_name[reg-0x80]);
	else 
		printf ("r[%x]", reg);
		
}


/**
 *
 */

static void _hexdump (u_char *ptr, u_int num)
{
	int i;

	for (i=8-num; i<8; i++)
		printf ("%02x ", ptr[i]);
	printf ("\t");	
}


/**
 *
 */

static char *_decode_calc (char val)
{
	static char calc_op[][10] = {
		"=", 
		"<->",	// swap
		"+=",
		"-=",
		"*=",
		"/=",
		"%=",
		"??",	// rnd
		"&=",
		"|=",
		"^=",
		"??",	// invalid
		"??",	// invalid
		"??",	// invalid
		"??"};	// invalid

	return (char *) calc_op[val&0x0F];
}

	
/**
 *
 */

static void _dump_cmp (u_char *opcode)
{
	op_t *op = (op_t *) opcode;
	u_int rval, lval;

	char cmp_op[][10] = {
		"none",
		"&&",
		"==",
		"!=",
		">=",
		">",
		"<",
		"<="};

	if (op->cmp) {
		printf ("if (");

		if ((op->cmd == 0x02) || (op->cmd == 0x00)) {	// lnk, goto
			lval = op->val4;
			rval = op->val3<<8 | op->val2;
		} else {
			lval = op->val1;
			rval = op->val0;
		}

		_PrintRegister (lval);
		printf (" %s ", cmp_op [op->cmp&0x07]);

		if (op->cmp & 0x08)
			printf ("0x%02X", rval);
		else 
			_PrintRegister (rval);
		printf (") ");
	}
}


/**
 *
 */

static void _cmd_nop (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	switch (op->sub_cmd) {
		case 1:
			printf ("goto line #0x%02X", opcode[7]);
			break;
		case 2:
			printf ("break");
			break;
		case 3:
			printf ("setTmpPM");
			break;
		default:
			printf ("nop");
			break;
	}
}


/**
 *
 */

static void _cmd_goto (u_char *opcode)
{
	printf ("goto line# 0x%02X", opcode[5]);
}


/**
 *
 */

static void _cmd_lnk (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	printf ("lnk to ");

	switch (op->sub_cmd) {
		case 0x01:
			printf ("SIns ");
			_hexdump (opcode, 8);
			break;

		case 0x04:
			printf ("PGC 0x%02X", opcode[6]<<8 | opcode[7]);
			break; 

		case 0x05:
			printf ("PTT 0x%02X, highlight 0x%02X", (op->val1&0x03<<8)|op->val0, op->val0>>2);
			break; 

		case 0x06:
			printf ("Program 0x%02X this PGC", opcode[7]);
			if (opcode[6])
				printf (", highlight 0x%02X", opcode[6]>>2);
			break;

		case 0x07:
			printf ("Cell 0x%02X this PGC", opcode[7]);
			if (opcode[6])
				printf (", highlight 0x%02X", opcode[6]>>2);
			break;
	}
}


/**
 *
 */

static void _cmd_jmp (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	printf ("jmp ");

	switch (op->sub_cmd) {
		case 0x01:
			printf ("Exit");
			break;
		case 0x02:
			printf ("VTS 0x%02X", opcode[5]);
			break;
		case 0x03:
			printf ("This VTS Title 0x%02X", opcode[5]);
			break;
		case 0x05:
			printf ("This VTS Title 0x%02X Part 0x%02X", opcode[5], opcode[2]<<8|opcode[3]);
			break;
		case 0x06:
			printf ("in SystemSpace ");
			switch (opcode[5]>>4) {
				case 0x00:
					printf ("to play first PGC");
					break;
				case 0x01: {
					printf ("to menu \"%s\"", decode_menuname (op->val2));
				}
					break;
				case 0x02:
					printf ("to VTS 0x%02X and TTN 0x%02X", opcode[3], opcode[4]);
					break;
				case 0x03:
					printf ("to VMGM PGC number 0x%02X", opcode[2]<<8 | opcode[3]);
					break;
				case 0x08:
					printf ("lu 0x%02X vts0x%02X menu \"%s\"", op->val4, op->val3, decode_menuname (op->val2));
					break;
			}
			break;
		case 0x08:
			switch(op->val2>>4) {
				case 0x00:
					printf ("system first pgc");
					break;
				case 0x01:
					printf ("system title menu");
					break;
				case 0x02:
					printf ("system menu \"%s\"", decode_menuname (op->val2));
					break;
				case 0x03:
					printf ("system vmg pgc %x ????", op->val5<<8|op->val4);
					break;
				case 0x08:
					printf ("system lu 0x%02X menu \"%s\"", opcode[4], decode_menuname (op->val2));
					break;
				case 0x0c:
					printf ("system vmg pgc 0x%02X", op->val5<<8|op->val4);
					break;
			}
			break;
	}
}


/**
 *
 */

static void _cmd_set2 (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	_PrintRegister (op->val4);
	printf (" %s ", _decode_calc (op->foo));
	_PrintRegister (op->val2);
}


/**
 *
 */

static void _cmd_set (u_char *opcode)
{
	op_t *op = (op_t *) opcode;
	_PrintRegister (op->val4);
	printf (" %s ", _decode_calc (op->foo));
	printf ("0x%04X", op->val3<<8 | op->val2);
}


/**
 *
 */

static void _cmd_setsystem2 (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	printf ("sys2: ");
	_PrintRegister (op->val3);
	printf (" %s ", _decode_calc (op->foo));
	_PrintRegister (op->val2);
}


/**
 *
 */

static void _cmd_setsystem (u_char *opcode)
{
	op_t *op = (op_t *) opcode;

	_PrintRegister (op->val4);
	printf (" %s ", _decode_calc (op->foo));
	printf ("0x%04X", op->val3<<8 | op->val2);
}


/**
 *
 */

static void _cmd_unknown (u_char *opcode)
{
	printf ("unknown");
}


/**
 * 8 bytes opcode
 */

void ifoPrintVMOP (u_char *opcode)
{
	op_t *op = (op_t *) opcode;
	u_char *ptr = opcode;

	_hexdump (opcode, 8);

// do we have a compare operation?
	_dump_cmp (opcode);

	cmd_switch [op->cmd].cmd (opcode);
	printf (";");

#if 0
	switch (op->cmd) {
	case CMD_GOTO: {
		printf ("goto line 0x%04X", ptr[6]<<8|ptr[7]);
	}
	break;
	//case 0x61: 
	case CMD_SET_SYSTEM: { // SetSystem
		printf ("r[%x] ", ptr[3]);
		printf (" %s ", _decode_calc (op->cmp));
		_PrintRegister (ptr[5]);
		//printf (" 0x%04X", ptr[4]<<8|ptr[5]);
	}
	break;	
	case 0x70: { // SetSystem
		_PrintRegister (ptr[3]);
		printf (" %s ", _decode_calc(op->cmp));
		printf (" 0x%04X", ptr[4]<<8|ptr[5]);
	}
	break;
	default:
		_hexdump (opcode, 8);
	}
#endif
}
