/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus - opprintf.c * * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * * Copyright (C) 2008 ZZT32 * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include "opprintf.h" /* Command Access Macros */ #define _OPCODE(x) (((x) >> 26) & 0x3F) #define _RS(x) (((x) >> 21) & 0x1F) #define _RT(x) (((x) >> 16) & 0x1F) #define _RD(x) (((x) >> 11) & 0x1F) #define _FS(x) (_RD(x)) #define _FT(x) (_RT(x)) #define _FD(x) (((x) >> 6) & 0x1F) #define _BASE(x) (_RS(x)) #define _IMMEDIATE(x) ((x) & 0xFFFF) #define _OFFSET(x) (_IMMEDIATE(x)) #define _B_OFFSET(x) (((s16)_IMMEDIATE(x)) * (s16)4) #define _TARGET_RAW(x) ((x) & 0x3FFFFFF) #define _TARGET(x) (_TARGET_RAW(x) << 2) #define _FUNC(x) ((x) & 0x3F) #define _SHAM(x) (((x) >> 6) & 0x1F) #define _FMT(x) (_RS(x)) #define _COP(x) ((x) >> 26 & 3) /* FMT modes */ static char fpu_fmt_names[] = { 'S', 'D', /* single/double */ '?', '?', /* reserved */ 'W', 'L' /* word/longword */ }; /* Floating point condition */ static char *fp_compare_types[] = { "F", "UN", "EQ", "UEQ", "OLT", "ULT", "OLE", "ULE", "SF", "NGLE", "SEQ", "NGL", "LT", "NGE", "LE", "NGT" }; /* General purpose register names */ static char *registers_a_gpr[] = { "R0", "AT", "V0", "V1", "A0", "A1", "A2", "A3", "T0", "T1", "T2", "T3", "T4", "T5", "T6", "T7", "S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "T8", "T9", "K0", "K1", "GP", "SP", "S8", "RA" }; /* Floating point register names */ static char *registers_a_fpr[] = { "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31" }; /* Coprocessor register names */ static char *registers_a_cop[] = { "Index", "Random", "EntryLo0", "EntryLo1", "Context", "PageMask", "Wired", "Reserved", "BadVAddr", "Count", "EntryHi", "Compare", "Status", "Cause", "EPC", "PRevID", "Config", "LLAddr", "WatchLo", "WatchHi", "XContext", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "PErr", "CacheErr", "TagLo", "TagHi", "ErrorEPC", "Reserved" }; /* The configuration word */ static long mr4kd_conf = MR4KD_SPACING(16) | MR4KD_RPREFIX | MR4KD_RLOWER | MR4KD_OLOWER; /* Copy a register name to dest */ static int mr4kd_rcpy_gpr ( char *dest, int reg ) { int i = 0, s = 0; /* Do we put a '$' ? */ if( mr4kd_conf & MR4KD_RPREFIX ) dest[i++] = '$'; for( ; registers_a_gpr[reg][s]; i++, s++ ) dest[i] = ( (mr4kd_conf & MR4KD_RLOWER) ? tolower(registers_a_gpr[reg][s]) : registers_a_gpr[reg][s] ); dest[i] = 0; return i; } /* Copy a register name to dest */ static int mr4kd_rcpy_fpr ( char *dest, int reg ) { int i = 0, s = 0; /* Do we put a '$' ? */ if( mr4kd_conf & MR4KD_RPREFIX ) dest[i++] = '$'; for(; registers_a_gpr[reg][s]; i++, s++ ) dest[i] = ( (mr4kd_conf & MR4KD_RLOWER) ? tolower(registers_a_fpr[reg][s]) : registers_a_fpr[reg][s] ); dest[i] = 0; return i; } #define TOKEN(x, y) ((x) << 8 | (y)) /* Give some pretty, formatted output */ int mr4kd_sprintf ( char *dest, char *name, uint32 instruction, uint32 pc, char *fmt ) { /* ** Format specifiers: ** - %rs ** - %rt ** - %rd ** ** - %fs ** - %ft ** - %fd ** ** - %cp Coprocessor register ** ** - %ff FP mode (single, double etc) ** - %fc FP compare condition ** ** - %ih Immediate (hex) ** - %id Immediate (dec) ** - %br Branch address ** - %jm Jump target ** ** - %co COP # ** ** - %ns Name ** - %nc Name with COP number ** - %nf Name with FP format ** ** - %sa Shift amount ** ** - %SP Remainder spacing */ int s = 0; /* Source */ int d = 0; /* Dest */ uint16 token; /* Scan the format string for specifiers */ for( s = 0; fmt[s]; s++ ) { /* No token? */ if( fmt[s] != '%' ) { dest[d++] = fmt[s]; continue; } /* Got token, read it in */ token = (fmt[s + 1] << 8 | fmt[s + 2]); /* Check */ switch( token ) { /* GPRs */ case TOKEN('r', 's'): d += mr4kd_rcpy_gpr( &dest[d], _RS(instruction) ); break; case TOKEN('r', 't'): d += mr4kd_rcpy_gpr( &dest[d], _RT(instruction) ); break; case TOKEN('r', 'd'): d += mr4kd_rcpy_gpr( &dest[d], _RD(instruction) ); break; /* FPRs */ case TOKEN('f', 's'): d += mr4kd_rcpy_fpr( &dest[d], _RS(instruction) ); break; case TOKEN('f', 't'): d += mr4kd_rcpy_fpr( &dest[d], _RT(instruction) ); break; case TOKEN('f', 'd'): d += mr4kd_rcpy_fpr( &dest[d], _RD(instruction) ); break; /* COP */ case TOKEN('c', 'p'): d += sprintf( &dest[d], "%s", registers_a_cop[_RD(instruction)] ); break; /* Immediate (hex) */ case TOKEN('i', 'h'): d += sprintf( &dest[d], MR4KD_FLAG_GET(MR4KD_HLOWER) ? "0x%04x" : "0x%04X", _IMMEDIATE(instruction) ); break; /* Immediate (decimal) */ case TOKEN('i', 'd'): d += sprintf( &dest[d], "%i", (int)((short)_IMMEDIATE(instruction)) ); break; /* Branch address */ case TOKEN('b', 'r'): d += sprintf( &dest[d], MR4KD_FLAG_GET(MR4KD_HLOWER) ? "0x%08x" : "0x%08X", (((int)(pc & 0xFFFFFFF) + (short)(instruction & 0xFFFF) * 4) | 0x80000000) ); break; /* Jump target */ case TOKEN('j', 'm'): d += sprintf( &dest[d], MR4KD_FLAG_GET(MR4KD_HLOWER) ? "0x%08x" : "0x%08X", _TARGET(instruction) | 0x80000000 ); break; /* Coprocessor number */ case TOKEN('c', 'o'): d += sprintf( &dest[d], "%u", _COP(instruction) ); break; /* Shift amount */ case TOKEN('s', 'a'): d += sprintf( &dest[d], "%u", _SHAM(instruction) ); break; /* Floating point compare mode */ case TOKEN('f', 'c'): d += sprintf( &dest[d], "%s", fp_compare_types[instruction & 0xF] ); break; /* Floating point mode */ case TOKEN('f', 'f'): d += sprintf( &dest[d], "%c", fpu_fmt_names[_FMT(instruction) - 16] ); break; /* Spacing! */ case TOKEN('S', 'P'): d += sprintf( &dest[d], "%*s", -((mr4kd_conf >> 24) - d), " "); break; /* Instruction name - regular (no space) */ case TOKEN('n', '0'): { int i; char nb[32]; /* Convert to the proper case */ for( i = 0; name[i]; i++ ) nb[i] = ( mr4kd_conf & MR4KD_OLOWER ) ? tolower(name[i]) : toupper(name[i]); /* Copy it to dest */ d += sprintf( &dest[d], "%.*s", i, nb ); } break; /* Instruction name - regular */ case TOKEN('n', 's'): { int i; char nb[32]; /* Convert to the proper case */ for( i = 0; name[i]; i++ ) nb[i] = ( mr4kd_conf & MR4KD_OLOWER ) ? tolower(name[i]) : toupper(name[i]); /* Copy it to dest */ d += sprintf( &dest[d], "%*.*s", -(mr4kd_conf >> 24), i, nb ); } break; /* Instruction name - with COP# */ case TOKEN('n', 'c'): { int i; char nb[32]; /* Convert to the proper case */ for( i = 0; name[i]; i++ ) nb[i] = ( mr4kd_conf & MR4KD_OLOWER ) ? tolower(name[i]) : toupper(name[i]); /* Copy number */ i += sprintf( &nb[i], "%u", _COP(instruction) ); /* Copy it to dest */ d += sprintf( &dest[d], "%*.*s", -(mr4kd_conf >> 24), i, nb ); } break; /* Floating point format */ case TOKEN('n', 'f'): { int i; char nb[32]; /* Convert to the proper case */ for( i = 0; name[i]; i++ ) nb[i] = ( mr4kd_conf & MR4KD_OLOWER ) ? tolower(name[i]) : toupper(name[i]); /* Copy the precision mode */ i += sprintf( &nb[i], ".%c",( mr4kd_conf & MR4KD_OLOWER ) ? tolower(fpu_fmt_names[_FMT(instruction)-16]) : toupper(fpu_fmt_names[_FMT(instruction)-16]) ); /* Copy it to dest */ d += sprintf( &dest[d], "%*.*s", -(mr4kd_conf >> 24), i, nb ); } break; } /* Update source pointer */ s += 2; } /* Null terminate output */ dest[d] = 0; /* Return chars processed */ return d; } /* Set a flag */ void mr4kd_flag_set ( int flag ) { MR4KD_FLAG_SET( flag ); } /* Clear a flag */ void mr4kd_flag_clear ( int flag ) { MR4KD_FLAG_CLEAR( flag ); } /* Get flag status */ int mr4kd_flag_get ( int flag ) { return (mr4kd_conf & flag); } /* Set spacing (between op & operands) */ void mr4kd_spacing ( int space ) { MR4KD_SET_SPACE( space ); }