/* Hey EMACS -*- linux-c -*- */ /* TiEmu - Tiemu Is an EMUlator * * Copyright (c) 2000-2001, Thomas Corvazier, Romain Lievin * Copyright (c) 2001-2003, Romain Lievin * Copyright (c) 2003, Julien Blache * Copyright (c) 2004, Romain Liévin * Copyright (c) 2005-2006, Romain Liévin, Kevin Kofler * * 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. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "ti68k_int.h" /* ti68k_debug_disassemble is a wrapper around the GDB or UAE disassembler, so we can use the GDB disassembler in GDB-enabled builds and the UAE one in non-GDB builds. The abstraction also allows plugging in any other disassembler instead at any moment. */ #ifndef NO_GDB /* GDB enabled: use the GDB disassembler */ #include "../../gdb/include/dis-asm.h" struct gdbarch; struct ui_file; struct disassemble_info gdb_disassemble_info (struct gdbarch *gdbarch, struct ui_file *file); int print_insn_m68k (bfd_vma memaddr, disassemble_info *info); uint32_t ti68k_debug_disassemble(uint32_t addr, char **line) { static struct disassemble_info di; static char *output = NULL; uint32_t offset; gchar **split; gchar *p; if (!output) { di = gdb_disassemble_info (NULL, NULL); output = di.stream; } *output = 0; offset = print_insn_m68k(addr, &di); split = g_strsplit(output, " ", 2); if(split[1]) for(p = split[1]; *p == ' '; p++); else p = ""; *line = g_strdup_printf("%06x: %s %s", addr, split[0] ? split[0] : "", p); g_strfreev(split); return offset; } #else extern int m68k_disasm (char *output, uint32_t addr); static const char* instr[] = { "ORSR", /* ORI #,SR */ "ANDSR", /* ANDI #,SR */ "EORSR", /* EORI #,SR */ "MVSR2", /* MOVE SR, */ "MV2SR", /* MOVE ,SR */ "MVR2USP.L", /* MOVE An,USP */ "MVUSP2R.L", /* MOVE USP,An */ "MVMLE", /* MOVEM , */ "MVMEL", /* MOVEM , */ "MVPMR", /* MOVEP ,<(d16,Ay)>*/ "MVPRM", /* MOVEP , */ "TRAP.L", /* TRAP # */ "RESET.L", "NOP.L", "STOP.L", "RTE.L", "RTS.L", "JMP.L", "LEA.L", "BTST", /* do nothing */ "BT", /* BRA */ NULL }; static int match_opcode(const char *opcode) { int i; if(opcode == NULL) return -1; for(i = 0; instr[i] != NULL; i++) { if(!strncmp(opcode, (char *)instr[i], strlen(instr[i]))) return i; } return -1; } // testing patterns: 0x55, 0xAA, 0x02, 0xF5, 0x5F, 0xFA, 0xAF static gchar* create_reg_list(uint8_t value, char name) { gchar *str = g_strdup(""); gchar *str2; int i; int pre_bit = 0; int cur_bit; int start = -1; int end = -1; for(i = 0; i < 8; i++, value >>= 1) { cur_bit = value & 1; if(pre_bit == 0 && cur_bit == 1) start = i; if(pre_bit == 1 && cur_bit == 0) end = i-1; if(start == end && start != -1) { str2 = g_strdup_printf("%s%c%i/", str, name, i-1); g_free(str); str = str2; end = start = -1; } else if(end > start) { str2 = g_strdup_printf("%s%c%i-%c%i/", str, name, start, name, end); g_free(str); str = str2; end = start = -1; } pre_bit = cur_bit; } if((end=i-1) > (start+1) && start != -1) { str2 = g_strdup_printf("%s%c%i-%i", str, name, start, end); g_free(str); str = str2; } else if(start > 0 && end > 0) { str2 = g_strdup_printf("%s%c%i", str, name, start); g_free(str); str = str2; } else str[strlen(str) - 1] = '\0'; return str; } static gchar* create_rev_reg_list(uint8_t value, char name) { gchar *str = g_strdup(""); gchar *str2; int i; int pre_bit = 0; int cur_bit; int start = -1; int end = -1; for(i = 0; i < 8; i++, value <<= 1) { cur_bit = (value & (1 << 7)) >> 7; if(pre_bit == 0 && cur_bit == 1) start = i; if(pre_bit == 1 && cur_bit == 0) end = i-1; if(start == end && start != -1) { str2 = g_strdup_printf("%s%c%i/", str, name, i-1); g_free(str); str = str2; end = start = -1; } else if(end > start) { str2 = g_strdup_printf("%s%c%i-%c%i/", str, name, start, name, end); g_free(str); str = str2; end = start = -1; } pre_bit = cur_bit; } if((end=i-1) > (start+1) && start != -1) { str2 = g_strdup_printf("%s%c%i-%i", str, name, start, end); g_free(str); str = str2; } else if(start > 0 && end > 0) { str2 = g_strdup_printf("%s%c%i", str, name, start); g_free(str); str = str2; } else str[strlen(str) - 1] = '\0'; return str; } static gchar* create_reg_lists(uint16_t value) { gchar *a, *d; gchar *str; a = create_reg_list(MSB(value), 'A'); d = create_reg_list(LSB(value), 'D'); return str = g_strconcat(a, "/", d, NULL); } static char* create_rev_reg_lists(uint16_t value) { gchar *a, *d; gchar *str; d = create_rev_reg_list(MSB(value), 'D'); a = create_rev_reg_list(LSB(value), 'A'); return str = g_strconcat(d, "/", a, NULL); } // do the same work as m68k_disasm but some instructions dis-assembled by the // UAE engine use a weird/wrong naming scheme so we remap them here rather than // touching the newcpu.c & table68k files because this file may be updated when // upgrading the UAE engine. int m68k_dasm(char **line, uint32_t addr) { char output[1024]; int offset; gchar** split; int idx; // get UAE disassembly offset = m68k_disasm(output, addr); // split string into address, opcode and operand split = g_strsplit(output, " ", 3); // search for opcode to rewrite idx = match_opcode(split[1]); if(idx != -1) { gchar *tmp; switch(idx) { case 0: /* ORI to SR #,SR */ { char c = split[1][5]; g_free(split[1]); split[1] = g_strdup_printf("ORI.%c", c); tmp = g_strconcat(split[2], ",SR", NULL); g_free(split[2]); split[2] = tmp; } break; case 1: /* ANDI to SR #,SR */ { char c = split[1][6]; g_free(split[1]); split[1] = g_strdup_printf("ANDI.%c", c); tmp = g_strconcat(split[2], ",SR", NULL); g_free(split[2]); split[2] = tmp; } break; case 2: /* EORI to SR #,SR */ { char c = split[1][6]; g_free(split[1]); split[1] = g_strdup_printf("EORI.%c", c); tmp = g_strconcat(split[2], ",SR", NULL); g_free(split[2]); split[2] = tmp; } break; case 3: /* MOVE from SR: SR, */ { char c = split[1][6]; g_free(split[1]); split[1] = g_strdup_printf("MOVE.%c", c); tmp = g_strconcat("SR,", split[2], NULL); g_free(split[2]); split[2] = tmp; } break; case 4: /* MOVE to SR: ,SR */ { char c = split[1][6]; g_free(split[1]); split[1] = g_strdup_printf("MOVE.%c", c); tmp = g_strconcat(split[2], ",SR", NULL); g_free(split[2]); split[2] = tmp; } break; case 5: /* MOVE An,USP */ g_free(split[1]); split[1] = g_strdup("MOVE"); tmp = g_strconcat(split[2], ",USP", NULL); g_free(split[2]); split[2] = tmp; break; case 6: /* MOVE USP,An */ g_free(split[1]); split[1] = g_strdup("MOVE"); tmp = g_strconcat("USP,", split[2], NULL); g_free(split[2]); split[2] = tmp; break; case 7: /* MOVEM , */ { char c = split[1][6]; char *p, *q; gchar *tmp; uint16_t mask; g_free(split[1]); split[1] = g_strdup_printf("MOVEM.%c", c); p = split[2]; q = strchr(split[2], ','); q++; sscanf(p, "#$%" SCNx16, &mask); if(q[0] != '-') tmp = g_strdup_printf("%s,%s", q, create_reg_lists(mask)); else tmp = g_strdup_printf("%s,%s", q, create_rev_reg_lists(mask)); g_free(split[2]); split[2] = tmp; } break; case 8: /* MOVEM , */ { char c = split[1][6]; char *p, *q; gchar *tmp; uint16_t mask; g_free(split[1]); split[1] = g_strdup_printf("MOVEM.%c", c); p = split[2]; q = strchr(split[2], ','); q++; sscanf(p, "#$%" SCNx16, &mask); if(q[0] != '-') tmp = g_strdup_printf("%s,%s", create_reg_lists(mask), q); else tmp = g_strdup_printf("%s,%s", q, create_rev_reg_lists(mask)); g_free(split[2]); split[2] = tmp; } break; case 9: /* MOVEP ,<(d16,Ay)> */ case 10: /* MOVEP <(d16,Ay)>, */ { char c = split[1][6]; g_free(split[1]); split[1] = g_strdup_printf("MOVEP.%c", c); } break; case 11: /* TRAP # */ case 12: /* RESET.L */ case 13: /* NOP.L */ case 14: /* STOP.L */ case 15: /* RTE.L */ case 16: /* RTS.L */ case 17: /* JMP.L */ case 18: /* LEA.L */ { char *p = strchr(split[1], '.'); if(p) *p = '\0'; } break; case 20: /* BRA */ { gchar *tmp; tmp = g_strdup_printf("BRA.%c", split[1][3]); g_free(split[1]); split[1] = tmp; } break; case 19: /* BTST */ default: break; } } // search for [value] and reject it at end of line if(strchr(split[2], '[')) { char *p = strchr(split[2], '['); char *q = strrchr(split[2], ']'); char tmp[256]; p--; q++; strncpy(tmp, p, q - p); tmp[q-p] = '\0'; strcpy(p, q); strcat(split[2], tmp); } *line = g_strdup_printf("%s %s %s", split[0] ? split[0] : "", split[1] ? split[1] : "", split[2] ? split[2] : ""); g_strfreev(split); return offset; } uint32_t ti68k_debug_disassemble(uint32_t addr, char **line) { uint32_t offset; offset = m68k_dasm(line, addr); return offset; } #endif