/***************************************************************************** * command.c: evaluate commands to allow further processing (see vmg.c). ***************************************************************************** * Copyright (C) 2002 VideoLAN * $Id: command.c,v 1.6 2003/01/29 22:09:46 sam Exp $ * * Authors: Stéphane Borel * * Adapted from Ogle - A video player * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #ifdef WIN32 # include #endif #include "common.h" #include #include #include "dvdplay/dvdplay.h" #include "tools.h" #include "command.h" #include "vmg.h" /* freebsd compatibility */ #ifndef PRIu8 #define PRIu8 "d" #endif #ifndef PRIu16 #define PRIu16 "d" #endif /* * tables to print operations for trace mode */ static const char *cmp_op_table[] = { NULL, "&", "==", "!=", ">=", ">", "<=", "<" }; static const char *set_op_table[] = { NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^=" }; static const char *link_table[] = { "LinkNoLink", "LinkTopC", "LinkNextC", "LinkPrevC", NULL, "LinkTopPG", "LinkNextPG", "LinkPrevPG", NULL, "LinkTopPGC", "LinkNextPGC", "LinkPrevPGC", "LinkGoUpPGC", "LinkTailPGC", NULL, NULL, "RSM" }; static const char *system_reg_table[] = { "Menu Description Language Code", "Audio Stream Number", "Sub-picture Stream Number", "Angle Number", "Title Track Number", "VTS Title Track Number", "VTS PGC Number", "PTT Number for One_Sequential_PGC_Title", "Highlighted Button Number", "Navigation Timer", "Title PGC Number for Navigation Timer", "Audio Mixing Mode for Karaoke", "Country Code for Parental Management", "Parental Level", "Player Configurations for Video", "Player Configurations for Audio", "Initial Language Code for Audio", "Initial Language Code Extension for Audio", "Initial Language Code for Sub-picture", "Initial Language Code Extension for Sub-picture", "Player Regional Code", "Reserved 21", "Reserved 22", "Reserved 23" }; static const char *system_reg_abbr_table[] = { NULL, "ASTN", "SPSTN", "AGLN", "TTN", "VTS_TTN", "TT_PGCN", "PTTN", "HL_BTNN", "NVTMR", "NV_PGCN", NULL, "CC_PLT", "PLT", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; /***************************************************************************** * _Bits: Get count bits of command from byte and bit position. *****************************************************************************/ static uint32_t _Bits( cmd_t* p_cmd, int i_byte, int i_bit, int i_count ) { uint32_t i_val = 0; int i_bit_mask; while( i_count-- ) { if( i_bit > 7 ) { i_bit = 0; i_byte++; } i_bit_mask = 0x01 << (7 - i_bit); i_val <<= 1; if( p_cmd->pi_bits[i_byte] & i_bit_mask ) { i_val |= 1; } p_cmd->pi_examined[i_byte] |= i_bit_mask; i_bit++; } return i_val; } /***************************************************************************** * _Compare: Compare data using operation, return result from comparison. ***************************************************************************** * Helper function for the different if functions. *****************************************************************************/ static dvdbool_t _Compare( const dvdplay_ptr dvdplay, uint8_t i_operation, uint16_t i_data1, uint16_t i_data2 ) { switch( i_operation ) { case 1: return i_data1 & i_data2 ? 1 : 0; case 2: return i_data1 == i_data2 ? 1 : 0; case 3: return i_data1 != i_data2 ? 1 : 0; case 4: return i_data1 >= i_data2 ? 1 : 0; case 5: return i_data1 > i_data2 ? 1 : 0; case 6: return i_data1 <= i_data2 ? 1 : 0; case 7: return i_data1 < i_data2 ? 1 : 0; } _dvdplay_err( dvdplay, "invalid comparison code" ); return 0; } /***************************************************************************** * _Reg: Eval register code ***************************************************************************** * can either be system or general register. * SXXX_XXXX, where S is 1 if it is system register. ******************************************************************************/ static uint16_t _Reg( dvdplay_ptr dvdplay, uint8_t i_reg ) { if( i_reg & 0x80 ) { /* FIXME max 24 not 32 */ if( i_reg < sizeof(system_reg_abbr_table) / sizeof(char *) ) { _dvdplay_trace( dvdplay, system_reg_table[i_reg] ); } else { _dvdplay_warn( dvdplay, "unknown system register" ); } return dvdplay->registers.SPRM[i_reg & 0x1f]; } else { if( i_reg < 16 ) { _dvdplay_trace( dvdplay, "g[%" PRIu8 "]", i_reg ); } else { _dvdplay_warn( dvdplay, "unknown general register" ); } return dvdplay->registers.GPRM[i_reg & 0x0f]; } } /***************************************************************************** * _RegOrData_1: Eval register or immediate data. ***************************************************************************** * AAAA_AAAA BBBB_BBBB, if immediate use all 16 bits for data else use * lower eight bits for the system or general purpose register. *****************************************************************************/ static uint16_t _RegOrData_1( dvdplay_ptr dvdplay, dvdbool_t b_imm, int i_byte ) { if( b_imm ) { /* immediate */ uint16_t i_bits = _Bits( &dvdplay->cmd, i_byte, 0, 16 ); _dvdplay_trace( dvdplay, "0x%x", i_bits ); if( isprint( i_bits & 0xff ) && isprint( ( i_bits >> 8 ) & 0xff ) ) { _dvdplay_trace( dvdplay, " (\"%c%c\")", (char)( ( i_bits >>8 ) & 0xff ), (char)( i_bits & 0xff ) ); } return i_bits; } else { return _Reg( dvdplay, _Bits( &dvdplay->cmd, i_byte + 1, 0, 8 ) ); } } /***************************************************************************** * _RegOrData_2: Eval register or immediate data. ***************************************************************************** * xBBB_BBBB, if immediate use all 7 bits for data else use * lower four bits for the general purpose register number. * Evaluates gprm or data depending on bit, data is in byte n. *****************************************************************************/ uint16_t _RegOrData_2( dvdplay_ptr dvdplay, dvdbool_t b_imm, int i_byte ) { if( b_imm ) { /* immediate */ uint16_t i_bits = _Bits( &dvdplay->cmd, i_byte, 1, 7 ); _dvdplay_trace( dvdplay, "0x%x", i_bits ); return i_bits; } else { uint16_t i_reg = _Bits( &dvdplay->cmd, i_byte, 4, 4 ); if( i_reg < 16 ) { _dvdplay_trace( dvdplay, "g[%" PRIu8 "]", i_reg ); } else { _dvdplay_warn( dvdplay, "unknown general register" ); } return dvdplay->registers.GPRM[i_reg]; } } /***************************************************************************** * _If_1: Evaluate if (version 1). ***************************************************************************** * Has comparison data in byte 3 and 4-5 (immediate or register). *****************************************************************************/ static dvdbool_t _If_1( dvdplay_ptr dvdplay ) { uint8_t i_op = _Bits( &dvdplay->cmd, 1, 1, 3 ); if( i_op ) { if( i_op < sizeof(cmp_op_table) / sizeof(char*) && cmp_op_table[i_op] != NULL ) { uint32_t i_arg1; uint32_t i_arg2; _dvdplay_trace( dvdplay, "if( " ); i_arg1 = _Reg( dvdplay, _Bits( &dvdplay->cmd, 3, 0, 8 ) ); _dvdplay_trace( dvdplay, " %s ", cmp_op_table[i_op] ); i_arg2 = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 1, 0, 1 ), 4 ); _dvdplay_trace( dvdplay, " ) " ); return _Compare( dvdplay, i_op, i_arg1, i_arg2 ); } } return 1; } /***************************************************************************** * _If_2: Evaluate if (version 2). ***************************************************************************** * This version only compares register which are in byte 6 and 7. *****************************************************************************/ static dvdbool_t _If_2( dvdplay_ptr dvdplay ) { uint8_t i_op = _Bits( &dvdplay->cmd, 1, 1, 3 ); if( i_op ) { if( i_op < sizeof(cmp_op_table) / sizeof(char*) && cmp_op_table[i_op] != NULL ) { uint32_t i_arg1; uint32_t i_arg2; _dvdplay_trace( dvdplay, "if( " ); i_arg1 = _Reg( dvdplay, _Bits( &dvdplay->cmd, 6, 0, 8 ) ); _dvdplay_trace( dvdplay, " %s ", cmp_op_table[i_op] ); i_arg2 = _Reg( dvdplay, _Bits( &dvdplay->cmd, 7, 0, 8 ) ); _dvdplay_trace( dvdplay, " ) " ); return _Compare( dvdplay, i_op, i_arg1, i_arg2 ); } } return 1; } /***************************************************************************** * _If_3: Evaluate if (version 3). ***************************************************************************** * Has comparison data in byte 2 and 6-7 (immediate or register). *****************************************************************************/ static dvdbool_t _If_3( dvdplay_ptr dvdplay ) { uint8_t i_op = _Bits( &dvdplay->cmd, 1, 1, 3 ); if( i_op ) { if( i_op < sizeof(cmp_op_table) / sizeof(char*) && cmp_op_table[i_op] != NULL ) { uint32_t i_arg1; uint32_t i_arg2; _dvdplay_trace( dvdplay, "if( " ); i_arg1 = _Reg( dvdplay, _Bits( &dvdplay->cmd, 2, 0, 8 ) ); _dvdplay_trace( dvdplay, " %s ", cmp_op_table[i_op] ); i_arg2 = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 1, 0, 1 ), 6 ); _dvdplay_trace( dvdplay, " ) " ); return _Compare( dvdplay, i_op, i_arg1, i_arg2 ); } } return 1; } /***************************************************************************** * _If_4: Evaluate if (version 4). ***************************************************************************** * Has comparison data in byte 1 and 4-5 (immediate or register) * The register in byte 1 is only the lowe nibble (4bits). *****************************************************************************/ static dvdbool_t _If_4( dvdplay_ptr dvdplay ) { uint8_t i_op = _Bits( &dvdplay->cmd, 1, 1, 3 ); if( i_op ) { if( i_op < sizeof(cmp_op_table) / sizeof(char*) && cmp_op_table[i_op] != NULL ) { uint32_t i_arg1; uint32_t i_arg2; _dvdplay_trace( dvdplay, "if( " ); i_arg1 = _Reg( dvdplay, _Bits( &dvdplay->cmd, 1, 4, 4 ) ), _dvdplay_trace( dvdplay, " %s ", cmp_op_table[i_op] ); i_arg2 = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 1, 0, 1 ), 4 ); _dvdplay_trace( dvdplay, " ) " ); return _Compare( dvdplay, i_op, i_arg1, i_arg2 ); } } return 1; } /***************************************************************************** * _SpecialInstruction: Evaluate special instruction ***************************************************************************** * returns the new row/line number 0 if no new row and 256 if Break. *****************************************************************************/ static int _SpecialInstruction( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { int i_instr, i_line, i_level; switch( ( i_instr = _Bits( &dvdplay->cmd, 1, 4, 4 ) ) ) { case 0: /* NOP */ i_line = 0; _dvdplay_trace( dvdplay, "Nop" ); return b_cond ? i_line : 0; case 1: /* Goto line */ i_line = _Bits( &dvdplay->cmd, 7, 0, 8 ); _dvdplay_trace( dvdplay, "Goto %" PRIu8, i_line ); return b_cond ? i_line : 0; case 2: /* Break * max number of rows < 256, so we will end this set */ i_line = 256; _dvdplay_trace( dvdplay, "Break" ); return b_cond ? 256 : 0; case 3: /* Set temporary parental level and goto */ i_line = _Bits( &dvdplay->cmd, 7, 0, 8 ); i_level = _Bits( &dvdplay->cmd, 6, 4, 4 ); _dvdplay_trace( dvdplay, "SetTmpPML %" PRIu8 ", Goto %" PRIu8, i_level, i_line ); if( b_cond ) { /* This always succeeds now, if we want real parental protection * we need to ask the user and have passwords and stuff. */ dvdplay->registers.SPRM[13] = i_level; } return b_cond ? i_line : 0; } _dvdplay_err( dvdplay, "unknown special instruction (%d)", i_instr ); return 0; } /***************************************************************************** * _LinkSubIns: Evaluate link by subinstruction. ***************************************************************************** * Return 1 if link, or 0 if no link * Actual link instruction is in return_values parameter. *****************************************************************************/ static dvdbool_t _LinkSubIns( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { uint16_t i_button = _Bits( &dvdplay->cmd, 6, 0, 6 ); uint8_t i_linkop = _Bits( &dvdplay->cmd, 7, 3, 5 ); if( i_linkop > 0x10 ) { _dvdplay_err( dvdplay, "unknown Link by Sub-Instruction command (%d)", i_linkop ); return 0; } _dvdplay_trace( dvdplay, "%s (button %" PRIu8 ")", link_table[i_linkop], i_button ); /* Assumes that the link_cmd_t enum has the same values as * the LinkSIns codes */ dvdplay->link.command = i_linkop; dvdplay->link.data1 = i_button; return b_cond; } /***************************************************************************** * _LinkInstruction: Evaluate link instruction. ***************************************************************************** * Returns 1 if link, or 0 if no link * Actual link instruction is in return_values parameter. *****************************************************************************/ static dvdbool_t _LinkInstruction( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { uint8_t i_op = _Bits( &dvdplay->cmd, 1, 4, 4 ); switch( i_op ) { case 1: return _LinkSubIns( dvdplay, b_cond ); case 4: dvdplay->link.command = LinkPGCN; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 6, 1, 15 ); _dvdplay_trace( dvdplay, "LinkPGCN %" PRIu16, dvdplay->link.data1 ); return b_cond; case 5: dvdplay->link.command = LinkPTTN; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 6, 6, 10 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 6, 0, 6 ); _dvdplay_trace( dvdplay, "LinkPTT %" PRIu16 " (button %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; case 6: dvdplay->link.command = LinkPGN; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 7, 1, 7 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 6, 0, 6 ); _dvdplay_trace( dvdplay, "LinkPGN %" PRIu8 " (button %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; case 7: dvdplay->link.command = LinkCN; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 7, 0, 8 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 6, 0, 6 ); _dvdplay_trace( dvdplay, "LinkCN %" PRIu8 " (button %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; } _dvdplay_err( dvdplay, "unknown link instruction" ); return 0; } /***************************************************************************** * _JumpInstruction: Evaluate a jump instruction. ***************************************************************************** * Returns 1 if jump or 0 if no jump * actual jump instruction is in return_values parameter. ******************************************************************************/ static dvdbool_t _JumpInstruction( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { switch( _Bits( &dvdplay->cmd, 1, 4, 4 ) ) { case 1: dvdplay->link.command = Exit; _dvdplay_trace( dvdplay, "Exit" ); return b_cond; case 2: dvdplay->link.command = JumpTT; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 1, 7 ); _dvdplay_trace( dvdplay, "JumpTT %" PRIu8, dvdplay->link.data1 ); return b_cond; case 3: dvdplay->link.command = JumpVTS_TT; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 1, 7 ); _dvdplay_trace( dvdplay, "JumpVTS_TT %" PRIu8, dvdplay->link.data1 ); return b_cond; case 5: dvdplay->link.command = JumpVTS_PTT; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 1, 7 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 2, 6, 10 ); _dvdplay_trace( dvdplay, "JumpVTS_PTT %" PRIu8 ":%" PRIu16, dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; case 6: switch( _Bits( &dvdplay->cmd, 5, 0, 2 ) ) { case 0: dvdplay->link.command = JumpSS_FP; _dvdplay_trace( dvdplay, "JumpSS FP" ); return b_cond; case 1: dvdplay->link.command = JumpSS_VMGM_MENU; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 4, 4 ); _dvdplay_trace( dvdplay, "JumpSS VMGM (menu %" PRIu8 ")", dvdplay->link.data1 ); return b_cond; case 2: dvdplay->link.command = JumpSS_VTSM; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 4, 0, 8 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 3, 0, 8 ); dvdplay->link.data3 = _Bits( &dvdplay->cmd, 5, 4, 4 ); _dvdplay_trace( dvdplay, "JumpSS VTSM (vts %" PRIu8 ", title " "%" PRIu8 ", menu %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2, dvdplay->link.data3 ); return b_cond; case 3: dvdplay->link.command = JumpSS_VMGM_PGC; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 2, 1, 15 ); _dvdplay_trace( dvdplay, "JumpSS VMGM (pgc %" PRIu8 ")", dvdplay->link.data1 ); return b_cond; } break; case 8: switch( _Bits( &dvdplay->cmd, 5, 0, 2 ) ) { case 0: dvdplay->link.command = CallSS_FP; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 4, 0, 8 ); _dvdplay_trace( dvdplay, "CallSS FP (rsm_cell %" PRIu8 ")", dvdplay->link.data1 ); return b_cond; case 1: dvdplay->link.command = CallSS_VMGM_MENU; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 4, 4 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 4, 0, 8 ); _dvdplay_trace( dvdplay, "CallSS VMGM (menu %" PRIu8 ", rsm_cell %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; case 2: dvdplay->link.command = CallSS_VTSM; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 5, 4, 4 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 4, 0, 8 ); _dvdplay_trace( dvdplay, "CallSS VTSM (menu %" PRIu8 ", rsm_cell %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; case 3: dvdplay->link.command = CallSS_VMGM_PGC; dvdplay->link.data1 = _Bits( &dvdplay->cmd, 2, 1, 15 ); dvdplay->link.data2 = _Bits( &dvdplay->cmd, 4, 0, 8 ); _dvdplay_trace( dvdplay, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")", dvdplay->link.data1, dvdplay->link.data2 ); return b_cond; } break; } _dvdplay_err( dvdplay, "unknown Jump/Call instruction" ); return 0; } /***************************************************************************** * _SystemSet: Evaluate a set system register instruction. ***************************************************************************** * May contain a link so return the same as eval_link. *****************************************************************************/ static dvdbool_t _SystemSet( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { int i; uint16_t i_data, i_data2; switch( _Bits( &dvdplay->cmd, 0, 4, 4 ) ) { case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */ for( i = 1 ; i <= 3 ; i++ ) { if( _Bits( &dvdplay->cmd, 2 + i, 0, 1 ) ) { _dvdplay_trace( "%s = ", system_reg_table[i] ); i_data = _RegOrData_2( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 2 + i ); _dvdplay_trace( dvdplay, " " ); if( b_cond ) { dvdplay->registers.SPRM[i] = i_data; } } } break; case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */ _dvdplay_trace( dvdplay, "%s = ", system_reg_table[9] ); i_data = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 2 ); _dvdplay_trace( dvdplay, " %s", system_reg_table[10] ); i_data2 = _Bits( &dvdplay->cmd, 5, 0, 8 ); // ?? size _dvdplay_trace( dvdplay, " = %" PRIu8, i_data2 ); if( b_cond ) { dvdplay->registers.SPRM[9] = i_data; // time dvdplay->registers.SPRM[10] = i_data2; // pgcN } break; case 3: /* Mode: Counter / Register + Set */ _dvdplay_trace( dvdplay, "SetMode " ); if( _Bits( &dvdplay->cmd, 5, 0, 1 ) ) { _dvdplay_err( dvdplay, "Detected SetGPRMMD Counter!! " "This is unsupported." ); _dvdplay_trace( dvdplay, "Counter " ); //exit(-1); } else { _dvdplay_trace( dvdplay, "Register " ); } i_data2 = _Bits( &dvdplay->cmd, 5, 4, 4 ); _Reg( dvdplay, i_data2 ); _dvdplay_trace( dvdplay, " = " ); i_data = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 2 ); if( b_cond ) { dvdplay->registers.GPRM[i_data2] = i_data; } break; case 6: /* Set system reg 8 (Highlighted button) */ // Not system reg!! _dvdplay_trace( dvdplay, "%s = ", system_reg_table[8] ); i_data = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 4 ); if( b_cond ) { dvdplay->registers.SPRM[8] = i_data; } break; } if( _Bits( &dvdplay->cmd, 1, 4, 4 ) ) { return _LinkInstruction( dvdplay, b_cond ); } return 0; } /***************************************************************************** * _SetOp: Evaluate set operation. ***************************************************************************** * Sets the register given to the value indicated by op and data. * For the swap case the contents of reg is stored in reg2. *****************************************************************************/ static void _SetOp( dvdplay_ptr dvdplay, int i_op, int i_reg, int i_reg2, int i_data ) { const int i_shortmax = ( 2 << 16 ) - 1; int i_tmp; switch( i_op ) { case 1: dvdplay->registers.GPRM[i_reg] = i_data; break; case 2: /* SPECIAL CASE - SWAP! */ dvdplay->registers.GPRM[i_reg2] = dvdplay->registers.GPRM[i_reg]; dvdplay->registers.GPRM[i_reg] = i_data; break; case 3: i_tmp = dvdplay->registers.GPRM[i_reg] + i_data; if( i_tmp >= i_shortmax ) { i_tmp = i_shortmax; } dvdplay->registers.GPRM[i_reg] = (uint16_t)i_tmp; break; case 4: i_tmp = dvdplay->registers.GPRM[i_reg] - i_data; if( i_tmp < 0 ) { i_tmp = 0; } dvdplay->registers.GPRM[i_reg] = (uint16_t)i_tmp; break; case 5: i_tmp = dvdplay->registers.GPRM[i_reg] * i_data; if( i_tmp >= i_shortmax ) { i_tmp = i_shortmax; } dvdplay->registers.GPRM[i_reg] = (uint16_t)i_tmp; break; case 6: if( i_data != 0 ) { dvdplay->registers.GPRM[i_reg] /= i_data; } else { /* really an error I guess */ dvdplay->registers.GPRM[i_reg] = 0; } break; case 7: dvdplay->registers.GPRM[i_reg] %= i_data; break; case 8: /* SPECIAL CASE - RANDOM! */ if( i_data != 0 ) { /* TODO rand() might only return 0-32768 */ dvdplay->registers.GPRM[i_reg] = ( rand() % i_data ) + 1; } else { /* really an error I guess */ dvdplay->registers.GPRM[i_reg] = 0; } break; case 9: dvdplay->registers.GPRM[i_reg] &= i_data; break; case 10: dvdplay->registers.GPRM[i_reg] |= i_data; break; case 11: dvdplay->registers.GPRM[i_reg] ^= i_data; break; } } /***************************************************************************** * _Set_1: Evaluate set instruction (version 1). ***************************************************************************** * Combined with either Link or Compare. ******************************************************************************/ static void _Set_1( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { uint8_t i_op = _Bits( &dvdplay->cmd, 0, 4, 4 ); uint8_t i_reg = _Bits( &dvdplay->cmd, 3, 4, 4 ); // Erhumm.. uint8_t i_reg2 = _Bits( &dvdplay->cmd, 5, 4, 4 ); uint16_t i_data; _Reg( dvdplay, i_reg ); if( i_op < sizeof(set_op_table) / sizeof(char *) && set_op_table[i_op] != NULL ) { _dvdplay_trace( dvdplay, "%s", set_op_table[i_op] ); } else { _dvdplay_err( dvdplay, "unknown set op" ); } i_data = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 4 ); if( b_cond ) { _SetOp( dvdplay, i_op, i_reg, i_reg2, i_data ); } } /***************************************************************************** * _Set_2: Evaluate set instruction (version 2). ***************************************************************************** * Combined with both Link and Compare. *****************************************************************************/ static void _Set_2( dvdplay_ptr dvdplay, dvdbool_t b_cond ) { uint8_t i_op = _Bits( &dvdplay->cmd, 0, 4, 4 ); uint8_t i_reg = _Bits( &dvdplay->cmd, 1, 4, 4 ); uint8_t i_reg2 = _Bits( &dvdplay->cmd, 3, 4, 4 ); // Erhumm.. uint16_t i_data; _Reg( dvdplay, i_reg ); if( i_op < sizeof(set_op_table) / sizeof(char *) && set_op_table[i_op] != NULL ) { _dvdplay_trace( dvdplay, "%s", set_op_table[i_op] ); } else { _dvdplay_err( dvdplay, "unknown set op" ); } i_data = _RegOrData_1( dvdplay, _Bits( &dvdplay->cmd, 0, 3, 1 ), 2 ); if( b_cond ) { _SetOp( dvdplay, i_op, i_reg, i_reg2, i_data ); } } /***************************************************************************** * _Command: Evaluate a command. ***************************************************************************** * returns row number of goto, 0 if no goto, -1 if link. * Link command in dvdplay is updated. *****************************************************************************/ static int _Command( dvdplay_ptr dvdplay, uint8_t * p_bytes ) { dvdbool_t b_cond; dvdbool_t b_extra_bits; int i; int i_res = 0; for( i = 0 ; i < 8 ; i++ ) { _dvdplay_trace( dvdplay, "%02x ", p_bytes[i] ); dvdplay->cmd.pi_bits[i] = p_bytes[i]; dvdplay->cmd.pi_examined[i] = 0; } _dvdplay_trace( dvdplay, "| " ); memset( &dvdplay->link, 0, sizeof(link_t) ); switch( _Bits( &dvdplay->cmd, 0, 0, 3 ) ) { /* three first bits */ case 0: /* Special instructions */ b_cond = _If_1( dvdplay ); i_res = _SpecialInstruction( dvdplay, b_cond ); if( i_res == -1 ) { _dvdplay_err( dvdplay, "unknown instruction!" ); //exit(0); } break; case 1: /* Link/jump instructions */ if( _Bits( &dvdplay->cmd, 0, 3, 1 ) ) { b_cond = _If_2( dvdplay ); i_res = _JumpInstruction( dvdplay, b_cond ); } else { b_cond = _If_1( dvdplay ); i_res = _LinkInstruction( dvdplay, b_cond ); } if( i_res ) { i_res = -1; } break; case 2: /* System set instructions */ b_cond = _If_2( dvdplay ); i_res = _SystemSet( dvdplay, b_cond ); if( i_res ) { i_res = -1; } break; case 3: /* Set instructions, either Compare or Link may be used */ b_cond = _If_3( dvdplay ); _Set_1( dvdplay, b_cond ); if( _Bits( &dvdplay->cmd, 1, 4, 4 ) ) { i_res = _LinkInstruction(dvdplay, b_cond ); } if( i_res ) { i_res = -1; } break; case 4: /* Set, Compare -> Link Sub-Instruction */ _Set_2( dvdplay, /*True*/ 1 ); b_cond = _If_4( dvdplay ); i_res = _LinkSubIns( dvdplay, b_cond ); if( i_res ) { i_res = -1; } break; case 5: /* Compare -> (Set and Link Sub-Instruction) */ b_cond = _If_4( dvdplay ); _Set_2( dvdplay, b_cond ); i_res = _LinkSubIns( dvdplay, b_cond ); if( i_res ) { i_res = -1; } break; case 6: /* Compare -> Set, allways Link Sub-Instruction */ b_cond = _If_4( dvdplay ); _Set_2( dvdplay, b_cond ); i_res = _LinkSubIns( dvdplay, /*True*/ 1 ); if( i_res ) { i_res = -1; } break; } /* Check if there are bits not yet examined */ b_extra_bits = 0; for(i = 0 ; i < 8 ; i++ ) { if( dvdplay->cmd.pi_bits[i] & ~dvdplay->cmd.pi_examined[i] ) { b_extra_bits = 1; break; } } if( b_extra_bits ) { _dvdplay_trace( dvdplay, "[WARNING, unknown bits:" ); for( i = 0 ; i < 8 ; i++ ) { _dvdplay_trace( dvdplay, " %02x", dvdplay->cmd.pi_bits[i] & ~dvdplay->cmd.pi_examined [i] ); } _dvdplay_trace( dvdplay, "]" ); } _dvdplay_trace( dvdplay, "\n" ); return i_res; } /***************************************************************************** * _dvdplay_CommandTable: Evaluate a set of commands and modify the given * register set. ******************************************************************************/ int _dvdplay_CommandTable( dvdplay_ptr dvdplay, vm_cmd_t * p_commands, int i_num_commands ) { int i_total = 0; int i = 0; #ifdef TRACE int j; _dvdplay_trace( dvdplay, " # " ); for( j = 0; j < 24; j++ ) { _dvdplay_trace( dvdplay, " %2d |", j ); } _dvdplay_trace( dvdplay, "\nSRPMS: " ); for( j = 0; j < 24; ++j ) { _dvdplay_trace( dvdplay, "%04x|", dvdplay->registers.SPRM[j] ); } _dvdplay_trace( dvdplay, "\nGRPMS: "); for( j = 0; j < 16; ++j ) { _dvdplay_trace( dvdplay, "%04x|", dvdplay->registers.GPRM[j] ); } _dvdplay_trace( dvdplay, "\n" ); _dvdplay_trace( dvdplay, "--------------------------------------------\n" ); #endif while( i < i_num_commands && i_total < 100000 ) { int i_line; i_line = _Command( dvdplay, &p_commands[i].bytes[0] ); if( i_line < 0 ) { /* Link command */ _dvdplay_trace( dvdplay, "doing Link/Jump/Call\n" ); return 1; } else if( i_line > 0 ) { /* Goto command */ i = i_line - 1; } else { /* Just continue on the next line */ ++i; } i_total++; } memset( &dvdplay->link, 0, sizeof(link_t) ); return 0; }