/* Copyright (C) 2008 Guillaume Duhamel Copyright (C) 2009-2010 DeSmuME team This file is part of DeSmuME DeSmuME 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. DeSmuME 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 DeSmuME; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "debug.h" #include "MMU.h" #include "armcpu.h" #include "arm_instructions.h" #include "thumb_instructions.h" #include "cp15.h" #include "NDSSystem.h" #include "utils/xstring.h" //#include "movie.h" #ifdef HAVE_LUA #include "lua-engine.h" #endif armcpu_t* TDebugEventData::cpu() { return procnum==0?&NDS_ARM9:&NDS_ARM7; } TDebugEventData DebugEventData; uint32_t debugFlag; //DEBUG CONFIGURATION const bool debug_acl = false; static bool acl_check_access(uint32_t adr, uint32_t access) { //non-user modes get separate access handling, so check that here if(NDS_ARM9.CPSR.bits.mode != USR) access |= 1; if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==false) { HandleDebugEvent(DEBUG_EVENT_ACL_EXCEPTION); } return true; } void HandleDebugEvent_ACL_Exception() { printf("ACL EXCEPTION!\n"); if(DebugEventData.memAccessType == MMU_AT_CODE) armcpu_exception(DebugEventData.cpu(),EXCEPTION_PREFETCH_ABORT); else if(DebugEventData.memAccessType == MMU_AT_DATA) armcpu_exception(DebugEventData.cpu(),EXCEPTION_DATA_ABORT); } /*static bool CheckRange(uint32_t adr, uint32_t min, uint32_t len) { return (adr>=min && adr static bool debugStatsSort(int num1, int num2) { if(which==0) { if(combinedHits[proc].arm[num2] == combinedHits[proc].arm[num1]) return false; if(combinedHits[proc].arm[num1] == 0xFFFFFFFF) return false; if(combinedHits[proc].arm[num2] == 0xFFFFFFFF) return true; return combinedHits[proc].arm[num2] < combinedHits[proc].arm[num1]; } else { if(combinedHits[proc].thumb[num2] == combinedHits[proc].thumb[num1]) return false; if(combinedHits[proc].thumb[num1] == 0xFFFFFFFF) return false; if(combinedHits[proc].thumb[num2] == 0xFFFFFFFF) return true; return combinedHits[proc].thumb[num2] < combinedHits[proc].thumb[num1]; } } void DebugStatistics::print() { //consolidate opcodes with the same names for(int i=0;i<2;i++) { combinedHits[i] = DEBUG_statistics.instructionHits[i]; for(int j=0;j<4096;j++) { if(combinedHits[i].arm[j] == 0xFFFFFFFF) continue; std::string name = arm_instruction_names[j]; for(int k=j+1;k<4096;k++) { if(combinedHits[i].arm[k] == 0xFFFFFFFF) continue; if(name == arm_instruction_names[k]) { //printf("combining %s with %d and %d\n",name.c_str(),combinedHits[i].arm[j],combinedHits[i].arm[k]); combinedHits[i].arm[j] += combinedHits[i].arm[k]; combinedHits[i].arm[k] = 0xFFFFFFFF; } } } for(int j=0;j<1024;j++) { if(combinedHits[i].thumb[j] == 0xFFFFFFFF) continue; std::string name = thumb_instruction_names[j]; for(int k=j+1;k<1024;k++) { if(combinedHits[i].thumb[k] == 0xFFFFFFFF) continue; if(name == thumb_instruction_names[k]) { //printf("combining %s with %d and %d\n",name.c_str(),combinedHits[i].arm[j],combinedHits[i].arm[k]); combinedHits[i].thumb[j] += combinedHits[i].thumb[k]; combinedHits[i].thumb[k] = 0xFFFFFFFF; } } } } InstructionHits sorts[2]; for(int i=0;i<2;i++) { for(int j=0;j<4096;j++) sorts[i].arm[j] = j; for(int j=0;j<1024;j++) sorts[i].thumb[j] = j; } std::sort(sorts[0].arm, sorts[0].arm+4096, debugStatsSort<0,0>); std::sort(sorts[0].thumb, sorts[0].thumb+1024, debugStatsSort<0,1>); std::sort(sorts[1].arm, sorts[1].arm+4096, debugStatsSort<1,0>); std::sort(sorts[1].thumb, sorts[1].thumb+1024, debugStatsSort<1,1>); for(int i=0;i<2;i++) { printf("Top arm instructions for ARM%d:\n",7+i*2); for(int j=0;j<10;j++) { int val = sorts[i].arm[j]; printf("%08d: %s\n", combinedHits[i].arm[val], arm_instruction_names[val]); } printf("Top thumb instructions for ARM%d:\n",7+i*2); for(int j=0;j<10;j++) { int val = sorts[i].thumb[j]; printf("%08d: %s\n", combinedHits[i].thumb[val], thumb_instruction_names[val]); } } } void DebugStatistics::printSequencerExecutionCounters() { for(int i=0;i<21;i++) printf("%06d ",sequencerExecutionCounters[i]); printf("\n"); } void DEBUG_reset() { //for now, just enable all debugging in developer builds #ifdef DEVELOPER debugFlag = 1; #endif DEBUG_Notify = DebugNotify(); DEBUG_statistics = DebugStatistics(); printf("DEBUG_reset: %08X\n",&DebugStatistics::print); //force a reference to this function } /*static void DEBUG_dumpMemory_fill(EMUFILE *fp, uint32_t size) { static std::vector buf; buf.resize(size); memset(&buf[0],0,size); fp->fwrite(&buf[0],size); }*/ void DEBUG_dumpMemory(EMUFILE* fp) { fp->fseek(0x000000,SEEK_SET); fp->fwrite(MMU.MAIN_MEM,0x800000); //arm9 main mem (8192K) fp->fseek(0x900000,SEEK_SET); fp->fwrite(MMU.ARM9_DTCM,0x4000); //arm9 DTCM (16K) fp->fseek(0xA00000,SEEK_SET); fp->fwrite(MMU.ARM9_ITCM,0x8000); //arm9 ITCM (32K) fp->fseek(0xB00000,SEEK_SET); fp->fwrite(MMU.ARM9_LCD,0xA4000); //LCD mem 656K fp->fseek(0xC00000,SEEK_SET); fp->fwrite(MMU.ARM9_VMEM,0x800); //OAM fp->fseek(0xD00000,SEEK_SET); fp->fwrite(MMU.ARM7_ERAM,0x10000); //arm7 WRAM (64K) fp->fseek(0xE00000,SEEK_SET); fp->fwrite(MMU.ARM7_WIRAM,0x10000); //arm7 wifi RAM ? fp->fseek(0xF00000,SEEK_SET); fp->fwrite(MMU.SWIRAM,0x8000); //arm9/arm7 shared WRAM (32KB) } //---------------------------------------------------- std::vector Logger::channels; static void defaultCallback(const Logger& logger, const char * message) { logger.getOutput() << message; } Logger::Logger() { out = &std::cout; callback = defaultCallback; flags = 0; } Logger::~Logger() { for(int i=0;i<(int)channels.size();i++) delete channels[i]; } void Logger::vprintf(const char * format, va_list l, const char * file, unsigned int line) { char buffer[1024]; char * cur = buffer; if (flags & Logger::FILE) cur += sprintf(cur, "%s:", file); if (flags & Logger::LINE) cur += sprintf(cur, "%d:", line); if (flags) cur += sprintf(cur, " "); ::vsnprintf(cur, 1024, format, l); callback(*this, buffer); } void Logger::setOutput(std::ostream * o) { out = o; } void Logger::setCallback(void (*cback)(const Logger& logger, const char * message)) { callback = cback; } void Logger::setFlag(unsigned int flag) { this->flags = flag; } void Logger::fixSize(unsigned int channel) { while(channel >= channels.size()) { channels.push_back(new Logger()); } } std::ostream& Logger::getOutput() const { return *out; } void Logger::log(unsigned int channel, const char * file, unsigned int line, const char * format, ...) { fixSize(channel); va_list l; va_start(l, format); channels[channel]->vprintf(format, l, file, line); va_end(l); } void Logger::log(unsigned int channel, const char *, unsigned int, std::ostream& os) { fixSize(channel); channels[channel]->setOutput(&os); } void Logger::log(unsigned int channel, const char *, unsigned int, unsigned int flag) { fixSize(channel); channels[channel]->setFlag(flag); } void Logger::log(unsigned int channel, const char *, unsigned int, void (*callback)(const Logger& logger, const char * message)) { fixSize(channel); channels[channel]->setCallback(callback); } void IdeasLog(armcpu_t* cpu) { uint32_t adr = cpu->R[0]; for(;;) { uint8_t c = _MMU_read08(cpu->proc_ID, MMU_AT_DEBUG, adr); adr++; if(!c) break; printf("%c",c); } //don't emit a newline. that is a pain in the butt. } void NocashMessage(armcpu_t* cpu) { uint32_t adr = cpu->instruct_adr + 6; std::string todo; for(;;) { uint8_t c = _MMU_read08(cpu->proc_ID, MMU_AT_DEBUG, adr); adr++; if(!c) break; todo.push_back(c); } //r0,r1,r2,...,r15 show register content (displayed as 32bit Hex number) //sp,lr,pc alias for r13,r14,r15 //scanline show current scanline number //frame show total number of frames since coldboot //totalclks show total number of clock cycles since coldboot //lastclks show number of cycles since previous lastclks (or zeroclks) //zeroclks resets the 'lastclks' counter //this is very inefficiently coded! char tmp[100]; todo = mass_replace(todo,"%sp%","%r13%"); todo = mass_replace(todo,"%lr%","%r14%"); todo = mass_replace(todo,"%pc%","%r15%"); sprintf(tmp,"%08X",cpu->R[0]); todo = mass_replace(todo,"%r0%",tmp); sprintf(tmp,"%08X",cpu->R[1]); todo = mass_replace(todo,"%r1%",tmp); sprintf(tmp,"%08X",cpu->R[2]); todo = mass_replace(todo,"%r2%",tmp); sprintf(tmp,"%08X",cpu->R[3]); todo = mass_replace(todo,"%r3%",tmp); sprintf(tmp,"%08X",cpu->R[4]); todo = mass_replace(todo,"%r4%",tmp); sprintf(tmp,"%08X",cpu->R[5]); todo = mass_replace(todo,"%r5%",tmp); sprintf(tmp,"%08X",cpu->R[6]); todo = mass_replace(todo,"%r6%",tmp); sprintf(tmp,"%08X",cpu->R[7]); todo = mass_replace(todo,"%r7%",tmp); sprintf(tmp,"%08X",cpu->R[8]); todo = mass_replace(todo,"%r8%",tmp); sprintf(tmp,"%08X",cpu->R[9]); todo = mass_replace(todo,"%r9%",tmp); sprintf(tmp,"%08X",cpu->R[10]); todo = mass_replace(todo,"%r10%",tmp); sprintf(tmp,"%08X",cpu->R[11]); todo = mass_replace(todo,"%r11%",tmp); sprintf(tmp,"%08X",cpu->R[12]); todo = mass_replace(todo,"%r12%",tmp); sprintf(tmp,"%08X",cpu->R[13]); todo = mass_replace(todo,"%r13%",tmp); sprintf(tmp,"%08X",cpu->R[14]); todo = mass_replace(todo,"%r14%",tmp); sprintf(tmp,"%08X",cpu->R[15]); todo = mass_replace(todo,"%r15%",tmp); sprintf(tmp,"%d",nds.VCount); todo = mass_replace(todo,"%scanline%",tmp); /*sprintf(tmp,"%d",currFrameCounter); */todo = mass_replace(todo,"%frame%",tmp); sprintf(tmp,"%lld",nds_timer); todo = mass_replace(todo,"%totalclks%",tmp); printf("%s",todo.c_str()); } //------- DebugNotify DEBUG_Notify; //enable bits arent being used right now. //if you want exhaustive logging, move the print before the early return (or comment the early return) //the intent of this system is to provide a compact dialog box showing which debug notifies have been //triggered in this frame (with a glowing LED!) and which debug notifies have been triggered EVER //which can be cleared, like a clip indicator in an audio tool. //obviously all this isnt implemented yet. void DebugNotify::NextFrame() { #ifdef DEVELOPER pingBits.reset(); #endif } void DebugNotify::ReadBeyondEndOfCart(uint32_t addr, uint32_t romsize) { #ifdef DEVELOPER if(!ping(DEBUG_NOTIFY_READ_BEYOND_END_OF_CART)) return; INFO("Reading beyond end of cart! ... %08X >= %08X\n",addr,romsize); #endif } bool DebugNotify::ping(EDEBUG_NOTIFY which) { bool wasPinged = pingBits[(int)which]; pingBits[(int)which] = true; return !wasPinged; }