// SNESDiasm.cpp : Defines the entry point for the application. // Copyright 2005 David Waggoner, AKA MathOnNapkins 2005 // notices: //1. try to add delphi component that XVI32 used //implement visual disassembly // //2. add an option to do no nonsense disassembly. #include "stdafx.h" #include "resource.h" #include "SnesDisasm.h" #include #include #include #include #include #include #include bool Acc16 = false; bool XY16 = false; bool MONSettings = false; bool Header = false; bool Alert = true; bool Visible = true; bool LinearOverride = false; bool HiRom = false; bool FastRom = false; bool RTF = false; bool SNESAddr = false; bool JumpTable = false; bool noErrors = false; int err = 0; int tss = 14 + sizeof(RECT) + 256; int selectedRare; bool* Flags[9] = { &Acc16, &XY16, &Header, &Alert, &HiRom, &LinearOverride, &FastRom, &RTF, &SNESAddr }; char filename[256]; char fileBuffer[256]; unsigned long bleh; int filesize; int settingsFileSize; int jumpSelector; int jumpTableTypes[4] = { IDC_JUMP1, IDC_JUMP2, IDC_JUMP3, IDC_JUMP4}; const int checkBoxes[9] = { IDC_ACC16, IDC_XY16, IDC_CHECKHEADER, IDC_ALERT, IDC_HIROM, IDC_LINEAR, IDC_FASTROM, IDC_COLOR, IDC_SNESADDR }; HINSTANCE thisOne; OPENFILENAME openfn; HANDLE file; HANDLE settings; HANDLE output; RECT coord; HWND thisWin; HMENU thisMenu; int hextodec(char *input, int length) { int result = 0; int value; int ceiling = length - 1; for(int j = ceiling, power16 = 16; j >= 0; j--) { if(input[j] >= 'A' && input[j] <= 'F') { value = input[j] - 'F'; value += 15; } else { value = input[j] - '9'; value += 9; } if(j == ceiling) { result += value; continue; } result += (value * power16); power16 *= 16; } return result; } int CALLBACK rareDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { unsigned char value; char meh[3]; int i; switch(msg) { case WM_COMMAND: switch(wparam) { case IDCANCEL: EndDialog(win, -1); break; case IDC_LOAD: load: GetDlgItemText(win, IDC_OPCODE, meh, 3); for(i = 0; i < 2; i++) { char j = meh[i]; if(j >= 'A' && j <= 'F') continue; if(j >= '0' && j <= '9') continue; else { MessageBox(win, "Edit box contains non-hex characters", "you naughty boy", MB_OK); return 0; } } selectedRare = value = (unsigned char) hextodec(meh, 2); if(wparam == IDC_TOGGLE) goto toggle; updateBox: if(rareOpcodes[selectedRare] == 1) SetDlgItemText(win, IDC_STATUS, "Rare"); else SetDlgItemText(win, IDC_STATUS, "Normal"); //get the current state of this flag and display it. break; case IDC_TOGGLE: goto load; toggle: if(selectedRare >= 0 && selectedRare < 256) { if(rareOpcodes[selectedRare] == 1) rareOpcodes[selectedRare] = 0; else rareOpcodes[selectedRare] = 1; } goto updateBox; //switch the status in real time break; } break; } return 0; } int GetSNESAddress(int old) { if(IsDlgButtonChecked(thisWin, IDC_FASTROM) == BST_CHECKED) FastRom = true; else FastRom = false; int bank = ((old & 0x7F8000) << 1) | 0x8000; if(HiRom) old |= 0xC00000; else { old &= 0x7FFF; old |= bank; if(FastRom) old |= 0x800000; } return old; } void GetSettings() { int loop; char* buffer = (char *) malloc(tss); GetCurrentDirectory(256, fileBuffer); strcat(fileBuffer, "\\Settings.mon"); if( !(settings = CreateFile(fileBuffer, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL))) return; settingsFileSize = GetFileSize(settings, 0); if(settingsFileSize < tss) return; if(!ReadFile(settings, buffer, tss, &bleh, 0)) { MessageBox(thisWin, "error in reading settings", "ouch", MB_OK); return; } for(loop = 0; loop < 9; loop++) { if(buffer[loop] == 1) { CheckDlgButton(thisWin, checkBoxes[loop], BST_CHECKED); *(Flags[loop] ) = true; } else { CheckDlgButton(thisWin, checkBoxes[loop], BST_UNCHECKED); *(Flags[loop] ) = false; } } for( ; loop < 13; loop++) { if(buffer[loop] == 1) { CheckMenuItem(thisMenu, jumpTableTypes[loop - 9], MF_CHECKED); jumpSelector = (loop - 9); } else CheckMenuItem(thisMenu, jumpTableTypes[loop - 9], MF_UNCHECKED); } if( buffer[loop++] == 1) SendMessage(thisWin, WM_COMMAND, IDC_STYLE, 0); coord = ((RECT*) (buffer + loop))[0]; loop += sizeof(RECT); MoveWindow(thisWin, coord.left, coord.top, coord.right - coord.left, coord.bottom - coord.top, true); for(int k = 0; k < 256; k++, loop++) rareOpcodes[k] = buffer[loop]; CloseHandle(settings); } void SaveSettings() { int loop; char* dummy = (char *) malloc(tss); settings = CreateFile(fileBuffer, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if(settings == INVALID_HANDLE_VALUE) { return; } for(loop = 0; loop < 9; loop++) { if(IsDlgButtonChecked(thisWin, checkBoxes[loop]) == BST_CHECKED) dummy[loop] = 1; else dummy[loop] = 0; } for(; loop < 13; loop++) { if(jumpSelector == (loop - 9)) dummy[loop] = 1; else dummy[loop] = 0; } if(MONSettings) dummy[loop++] = 1; else dummy[loop++] = 0; GetWindowRect(thisWin, &coord); memcpy(dummy + loop, &coord, sizeof(RECT) ); loop += sizeof(RECT); for(int k = 0; k < 256; k++, loop++) dummy[loop] = rareOpcodes[k]; WriteFile(settings, dummy, tss, &bleh, 0); if(bleh != (unsigned long) tss) MessageBox(thisWin, "settings mismatch", "error", MB_OK); CloseHandle(settings); } void WriteRTFHeader() { unsigned long temp; char *dummy; dummy = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\fo\\froman\\fprq2\\fcharset0 Times New Roman;}}\n"; WriteFile(output, dummy, strlen(dummy), &temp, 0); // temp should be 0x62... // 1 dark green 2 bright red 3 bright blue 4 orange 5 teal dummy = "{\\colortbl ;\\red0\\green128\\blue0;\\red255\\green0\\blue0;\\red0\\green0\\blue255;\\red255\\green102\\blue0;\\red0\\green128\\blue128;}\x0D\x0A"; WriteFile(output, dummy, strlen(dummy), &temp, 0); dummy = "\\viewkind4\\ucl\\pard"; WriteFile(output, dummy, strlen(dummy), &temp, 0); // the header has been written, now write the text with formatting. } //turns a long address into a rom address int GetAddressLong(unsigned int holderLong) { err = 0; if(HiRom) { holderLong ^= 0xC00000; } else { holderLong &= 0x7FFFFF; if((holderLong & 0x8000) == 0) { err = MessageBox(thisWin,"Illegal Jump Address detected in Lo-Rom mode.\nContinue?","eek",MB_YESNO); if(err == IDNO) err = 1; } holderLong = ( (holderLong & 0x7F0000) >> 1) + (holderLong & 0x7FFF); } return holderLong; } //converts an absolute address in a given bank to a rom address int GetAddress(unsigned short holder, int position) { if(HiRom) { //tells us what bank we're in, and we'll use it as an offset. position &= 0x7F0000; position += (int) holder; } else { position &= 0x7F8000; if((holder & 0x8000) == 0) { err = MessageBox(thisWin,"Illegal Jump Address detected in Lo-Rom mode.\nContinue?","eek",MB_YESNO); if(err == IDNO) err = 1; } position += (((int) holder) & 0x7FFF); } return position; } void GenerateJumpTable(char *buffer, int bufLength, int lower) { int bufIndex = 0; char **outputBuffer; char dummy[256]; char rangeBuf[256]; unsigned long numBytesWritten; char *LnBrk; char *tab; unsigned short holder; unsigned int holderLong; int address; int outputIndex = 0; if(IsDlgButtonChecked(thisWin, IDC_SNESADDR) == BST_CHECKED) SNESAddr = true; else SNESAddr = false; RTF = false; if(IsDlgButtonChecked(thisWin, IDC_COLOR) == BST_CHECKED) RTF = true; err = 0; if(IsDlgButtonChecked(thisWin,IDC_HIROM) == BST_CHECKED) HiRom = true; else HiRom = false; switch(jumpSelector) { case 0: case 2: if( (bufLength % 2) != 0) { MessageBox(thisWin, "Jump Table is of improper size", "error", MB_OK); return; } bufLength >>= 1; outputBuffer = (char **) malloc( sizeof(int) * bufLength); for(outputIndex = 0 ; bufIndex < bufLength ; bufIndex++, outputIndex++) { if(jumpSelector == 2) goto indexedAbs; holder = ((unsigned short*) buffer)[bufIndex]; goto makeString; indexedAbs: holder = (unsigned short) buffer[bufIndex] & 0xFF; holder += (unsigned short) ( ( buffer[ bufIndex + bufLength ] << 8) ); makeString: itoa((int) holder, dummy, 16); address = GetAddress(holder,lower + bufIndex); if(err == 1) goto badend; strcat(dummy, " = $"); if(SNESAddr) address = GetSNESAddress(address); itoa(address, dummy + strlen(dummy), 16); strupr(dummy); outputBuffer[outputIndex] = (char*) malloc(strlen(dummy)); strcpy(outputBuffer[outputIndex], dummy); //MessageBox(thisWin, dummy, "ok", MB_OK); } break; case 1: // handle the long jump tables case 3: if( (bufLength % 3) != 0) { MessageBox(thisWin, "Jump Table is of improper size", "error", MB_OK); return; } bufLength /= 3; outputBuffer = (char **) malloc( sizeof(int) * bufLength); for(outputIndex = 0; bufIndex < bufLength ; bufIndex++, outputIndex++) { if(jumpSelector == 3) goto indexedLong; holderLong = (unsigned int) (buffer[bufIndex * 3] & 0xFF); holderLong += (unsigned int) (buffer[(bufIndex * 3) + 1] << 8) & 0xFFFF; holderLong += (unsigned int) (buffer[(bufIndex * 3) + 2] << 16) & 0xFFFFFF; goto makeStringLong; indexedLong: holderLong = (unsigned int) buffer[bufIndex] & 0xFF; holderLong += (unsigned int) ( ( buffer[ bufIndex + bufLength ] << 8) ) & 0xFFFF; holderLong += (unsigned int) (buffer[bufIndex + (bufLength * 2)] << 16) & 0xFFFFFF; makeStringLong: itoa(holderLong, dummy, 16); address = GetAddressLong(holderLong); if(err == 1) goto badend; strcat(dummy, " = $"); if(SNESAddr) address = GetSNESAddress(address); itoa(address, dummy + strlen(dummy), 16); strupr(dummy); outputBuffer[outputIndex] = (char*) malloc(strlen(dummy)); strcpy(outputBuffer[outputIndex], dummy); //MessageBox(thisWin, dummy, "ok", MB_OK); } break; } if(RTF) { WriteRTFHeader(); LnBrk = "\\par\x0D\x0A"; tab = "\\tab "; strcpy(dummy,"\\cf3\\b\\f0\\fs24 "); WriteFile(output,dummy,strlen(dummy), &numBytesWritten, 0); } else { LnBrk = "\x0D\x0A"; tab = "\x09"; } //Write out the $xxxx-$yyyy crap. GetDlgItemText(thisWin, IDC_LOWER, rangeBuf, 256); WriteFile(output,"$",1,&numBytesWritten,0); WriteFile(output,rangeBuf,strlen(rangeBuf),&numBytesWritten,0); WriteFile(output,"-$",2,&numBytesWritten,0); GetDlgItemText(thisWin, IDC_UPPER, rangeBuf, 256); WriteFile(output,rangeBuf,strlen(rangeBuf),&numBytesWritten,0); strcpy(dummy, " Jump Table"); WriteFile(output, dummy, strlen(dummy), &numBytesWritten, 0); ZeroMemory(dummy, 256); if(RTF) strcpy(dummy, "\\b0"); strcpy(dummy + strlen(dummy), LnBrk); strcat(dummy, "["); strcat(dummy, LnBrk); WriteFile(output,dummy, strlen(dummy), &numBytesWritten, 0); for( outputIndex = 0; outputIndex < bufLength; outputIndex++ ) { WriteFile(output, tab, strlen(tab), &numBytesWritten, 0); strcpy(dummy,outputBuffer[outputIndex]); WriteFile(output, dummy, strlen(dummy), &numBytesWritten, 0); WriteFile(output, LnBrk, strlen(LnBrk), &numBytesWritten, 0); } strcpy(dummy, "]"); if(RTF) strcat(dummy, "}"); WriteFile(output, dummy, strlen(dummy), &numBytesWritten, 0); MessageBox(thisWin, "Jump Table Completed", "yay", MB_OK); badend: CloseHandle(output); } int DisasmLoop(char *buffer, int bufLength, int lower) { //index is the offset into buffer. int index = 0; //index2 will int index2 = 0; int bytesToRead = 0; int opcodeLocation = 0; int branchIndex = 0; unsigned char opcode; unsigned long numBytesWritten; char temp[256]; char rangeBuf[256]; char adjustedString[256]; //buffers to deal with various memory maps //maskBits Layout: // 80 40 20 10 08 04 02 01 // 7 6 5 4 3 2 1 0 /* | | | | | | | \--indication of being an opcode | | | | | | \--indicates a branch destination | | | | | \--indicates a branch point | | | | \--indicates Accumulator width | | | \--indicates XY width | | \--indicates this is data or an operand for an instruction (i.e. not an opcode) | \--Alternate entry points \--not reserved if a maskBits entry is zero, it has not been mapped out at all yet. */ char *maskBits; char *branchDest; char *branchPoints; char *colorInfo; char** outputBuffer; bool AccPrior = false; bool XYPrior = false; bool jumpboundary = false; bool alignment = true; char dummy[256]; char jumpLocation[256]; char *tab; char *LnBrk; char *leftBrack; char *rightBrack; char dummychar = 9; tab = &dummychar; char *colorList[6] = {"\\cf0", "\\cf1", "\\cf2", "\\cf3", "\\cf4", "\\cf5"}; char green = 1; char red = 2; char blue = 3; char orange = 4; char teal = 5; RTF = false; unsigned long numNewLines = 0; //lets get started!!!! //determine initial settings before the loop starts. We don't want them modified during the disassembly. if(IsDlgButtonChecked(thisWin, IDC_SNESADDR) == BST_CHECKED) SNESAddr = true; else SNESAddr = false; if(IsDlgButtonChecked(thisWin,IDC_LINEAR) == BST_CHECKED) LinearOverride = true; if(IsDlgButtonChecked(thisWin,IDC_HIROM) == BST_CHECKED) HiRom = true; else HiRom = false; if(IsDlgButtonChecked(thisWin,IDC_ACC16) == BST_CHECKED) Acc16 = AccPrior = true; else Acc16 = AccPrior = false; if(IsDlgButtonChecked(thisWin,IDC_XY16) == BST_CHECKED) XY16 = XYPrior = true; else XY16 = XYPrior = false; if(IsDlgButtonChecked(thisWin,IDC_COLOR) == BST_CHECKED) RTF = true; maskBits = (char*) malloc(bufLength + 1); branchDest = (char*) malloc(bufLength + 1); branchPoints = (char*) malloc(bufLength + 1); colorInfo = (char*) malloc(bufLength + 1); outputBuffer = (char **) malloc(sizeof(int)*bufLength); //zero all the memory. ZeroMemory(maskBits,bufLength + 1); ZeroMemory(branchDest,bufLength + 1); ZeroMemory(branchPoints,bufLength + 1); ZeroMemory(outputBuffer,bufLength*4); ZeroMemory(colorInfo, bufLength + 1); //*************************************** //STEP ONE: LOOPING THROUGH THE BUFFER anotherpass: while(index < bufLength) { //initialize this at every loop iteration. bytesToRead = 0; alignment = true; jumpboundary = false; // get the opcode (8 bit value) opcode = (unsigned char) buffer[index]; //do a few tests to see if this needs disassembling, or is in error. if( (maskBits[index] & 0x20) == 0x20) { if(!noErrors) MessageBox(thisWin, "Ambiguous byte detected. Please check your code.", "error", MB_OK); outputBuffer[index] = "ambiguous byte!!!"; break; } //if we already have an output string, there's no reason to be here! if( outputBuffer[index] != 0) break; //ok... so every thing seems okay. //handle BRK and COP alerts... frequent signs of trouble. if(Alert) { strcpy(dummy, "Opcode 0x"); if(rareOpcodes[opcode] == 1) { itoa(opcode, dummy + strlen(dummy), 16); strcat(dummy, " encountered, continue?"); int err = MessageBox( thisWin, dummy, "Alert", MB_YESNO); if(err == IDNO) { goto badend; } } } if( (maskBits[index] & 0x02) == 0x02) { if( (maskBits[index] & 0x08) == 0x08) { Acc16 = true; CheckDlgButton(thisWin, IDC_ACC16, BST_CHECKED); } else { Acc16 = false; CheckDlgButton(thisWin, IDC_ACC16, BST_UNCHECKED); } if( (maskBits[index] & 0x10) == 0x10) { XY16 = true; CheckDlgButton(thisWin, IDC_XY16, BST_CHECKED); } else { XY16 = false; CheckDlgButton(thisWin, IDC_XY16, BST_UNCHECKED); } } //indicates it's an opcode. maskBits[index] |= 0x01; //get the associated string that goes with the opcode. strcpy(temp,mnemonics[opcode]); opcodeLocation = index; //step ahead until completion index++; if(MONSettings) { //for my settings, I use different mnemonics for some instructions. if(opcode == 0x4B) strcpy(temp,"PHPB"); if(opcode == 0x8B) strcpy(temp,"PHDB"); if(opcode == 0xAB) strcpy(temp,"PLDB"); } //the lenght of associated string initializes to zero. index2 = 0; while(temp[index2] != 0) { // go until we hit the null terminator. if(temp[index2] == '$') { // if we encounter a dollar sign, // handle switchable accumulator, X/Y sizes. if(temp[index2 + 1] == 'A') bytesToRead = Acc16 ? 2 : 1; else if(temp[index2 + 1] == 'X') bytesToRead = XY16 ? 2 : 1; else bytesToRead = (int) (temp[index2+1] - '0'); //since we won't be interested in this next character //in writing the adjustedString, no index++. } index2++; } //account for the null terminator. index2++; //index2 is the length of the associated string. //generate a buffer that can fit the associated string //and extra bytes to be read. //if there are actually bytes needing to be read if(bytesToRead) { // don't forget the length of the assocated string. int oldIndex = index2; //offset the size of adjustedString by the number of bytes to read. // (each hex value is two chars) index2 += (bytesToRead * 2); // get to the point where the $ occurs. for(int j = 0; temp[j] != '$'; j++) adjustedString[j] = temp[j]; //fill in the $ sign, increment j. adjustedString[j++] = temp[j]; //oldIndex is used to keep track of where we're //reading from in the original associated string. oldIndex = j + 1; //we'll read at most 3 bytes. unsigned char readBytes[3]; // put the first byte read in the last spot. for( int i = bytesToRead - 1; i >= 0; i-- ) { char hexRep[2]; maskBits[index] |= 0x20; if((maskBits[index] & 0x01) == 01) if(!noErrors) MessageBox(thisWin, "Ambiguous byte detected. Please check your code.", "error", MB_OK); if(index == bufLength) { MessageBox(thisWin, "Your dissasembly ends in the middle of an instruction.\nIncrease the buffer length.", "dammit", MB_OK); goto badend; } readBytes[i] = buffer[index++]; //parse into a two digit hex number. //upper nibble int nibble = readBytes[i] & 0xF0; itoa(nibble,&hexRep[0],16); nibble = readBytes[i] & 0x0F; itoa(nibble,&hexRep[1],16); adjustedString[j+(i*2)] = hexRep[0]; adjustedString[j+(i*2)+1] = hexRep[1]; } j += (bytesToRead*2); //handle SEP if(opcode == 0xE2) { if(readBytes[0] & 0x20) { CheckDlgButton(thisWin, IDC_ACC16, BST_UNCHECKED); Acc16 = false; } if(readBytes[0] & 0x10) { CheckDlgButton(thisWin, IDC_XY16, BST_UNCHECKED); XY16 = false; } } //handle REP if(opcode == 0xC2) { if(readBytes[0] & 0x20) { CheckDlgButton(thisWin, IDC_ACC16, BST_CHECKED); Acc16 = true; } if(readBytes[0] & 0x10) { CheckDlgButton(thisWin, IDC_XY16, BST_CHECKED); XY16 = true; } } //fill in the remainder of the string for( ; j <= index2; j ++) adjustedString[j] = temp[oldIndex++]; int tail = 0; int dest = 0; jumpboundary = false; //handle branch statements switch(opcode) { case 0x6C: case 0xDC: colorInfo[opcodeLocation] = blue; if(!noErrors) MessageBox(thisWin,"External Jump detected.\nRoutine may be shorter than the region you have chosen.","error",MB_OK); jumpboundary = true; break; case 0x20: case 0x4C: case 0x7C: case 0xFC: colorInfo[opcodeLocation] = blue; //gives us a local pointer dest = (int) readBytes[1] + ( ((int) readBytes[0]) << 8); dest &= 0xFFFF; jumpmapping: //convert from a system address to a rom address. if(HiRom) { if(opcode == 0x22 || opcode == 0x5C) { dest ^= 0xC00000; } else { //gets the rom offset of the opcode. tail = lower + opcodeLocation; //tells us which "bank" the opcode is in. tail &= 0x7F0000; //gets the full rom address dest = tail + dest; } } else { //else we're using Lo-Rom. In which case all banks are 32K in length. if(opcode == 0x22 || opcode == 0x5C) { if((dest & 0x8000) == 0) if(!noErrors) MessageBox(thisWin,"Illegal JSR/JMP instruction detected in Lo-Rom mode.\nTried to jump into memory.","eek",MB_OK); tail = dest & 0x7F0000; dest &= 0x7FFF; dest = (tail >> 1) + dest; } else { tail = lower + opcodeLocation; tail &= 0x7F8000; dest = tail + (dest & 0x7FFF); } } //if the location is in the file range.. if(dest >= 0 && dest < filesize) { // we're going to tack the rom address onto the end as a comment. char jumpAddress[256]; int end; if(MONSettings) strcpy(jumpAddress, "; // $"); else strcpy(jumpAddress, " ; $"); itoa(dest, jumpAddress + 6, 16); strupr(jumpAddress); end = strlen(jumpAddress); strcpy(jumpAddress + end," IN ROM"); end = strlen(adjustedString); strncpy(adjustedString + end, jumpAddress, strlen(jumpAddress) + 1); // final output should look something like: // JSR $ABCD; // $XYZW IN ROM } else { //if the location goes beyond the range of the rom... we have problems MessageBox(thisWin,"One of your jumps extends beyond the rom's upper bound!","serious error",MB_OK); goto badend; } // if the jump is internal, go to it. if( dest >= lower && dest < (lower + bufLength + 1) ) { if(opcode == 0x20 || opcode == 0x22) { if((dest - lower) != bufLength) { maskBits[dest - lower] |= 0x41; } } if(opcode == 0x4C || opcode == 0x5C) { //designate this as a jump location. maskBits[dest - lower] |= 0x40; if((dest - lower) == bufLength) { if(!noErrors) MessageBox( thisWin, "Your diassembly be incomplete,\nsince there is a jump destination at the end of the\nbuffer.", "upper bound may be too short", MB_OK ); } else maskBits[dest - lower] |= 0x01; goto unconditional; } } else { if(opcode == 0x4C || opcode == 0x5C) { if(!noErrors) MessageBox(thisWin,"External Jump detected.\nRoutine may be shorter than the region you have chosen.","error",MB_OK); jumpboundary = true; } } break; case 0x22: case 0x5C: colorInfo[opcodeLocation] = blue; //gives us a long pointer dest = (int) readBytes[2] + ( ((int) readBytes[1]) << 8) + ( ((int) readBytes[0]) << 16); dest &= 0xFFFFFF; goto jumpmapping; break; case 0xB0: case 0x90: case 0x80: case 0x70: case 0x50: case 0xF0: case 0xD0: case 0x30: case 0x10: colorInfo[opcodeLocation] = red; if(opcode == 0x80) colorInfo[opcodeLocation] = blue; tail = opcodeLocation + 2; dest = tail + (char) readBytes[0]; assignLocations: if( dest >= 0 && dest < (bufLength + 1) && branchIndex < 128) { // if this is not already a branch destination... if((maskBits[dest] & 0x02) == 0) { //mark the destination address as a new branch destination. if(dest == bufLength) if(!noErrors) MessageBox( thisWin, "Your diassembly be incomplete,\nsince there is a branch label at the end of the\nbuffer.", "upper bound may be too short", MB_OK ); maskBits[dest] |= 0x02; //this tells us which branch string to use at that particular point. //alpha, beta, gamma... etc. branchDest[dest] = branchIndex; //the bits for a branch point are stored in the third bit. maskBits[opcodeLocation] |= 0x04; branchPoints[opcodeLocation] = branchIndex; branchIndex++; if(dest != bufLength) maskBits[dest] |= 0x01; if(Acc16) maskBits[dest] |= 0x08; if(XY16) maskBits[dest] |= 0x10; if(opcode == 0x80 || opcode == 0x82) goto unconditional; } else { //here, the destination is already noted as a location, so we just //figure out which index it was and indicate that in the opcodeLocation maskbyte. maskBits[opcodeLocation] |= 0x04; branchPoints[opcodeLocation] = branchDest[dest]; if(opcode == 0x80 || opcode == 0x82) goto unconditional; } // finish, if this destination wasn't in range, the address is computed below and written off. break; } dest = lower + dest; // compare against the offset of the overall file. if(dest >= 0 && dest < filesize) { char branchAddress[256]; strcpy(branchAddress,"BRANCH_$"); if(SNESAddr) dest = GetSNESAddress(dest); if(opcode == 0x80) jumpboundary = true; //tack the value on to the end of the BRANCH_$ portion. itoa(dest, branchAddress + 8,16); strupr(branchAddress); //copy beginning with the #$ or # character, which is 4 spots in. //e.g. BRA #FE -> BRA BRANCH_$40002 strncpy(adjustedString + 4, branchAddress, strlen(branchAddress) + 1); } else { MessageBox(thisWin, "You have a serious problem. \nOne or more of your branches or jumps has a \nDestination outside of the rom file.", "you dun screwed up", MB_OK); goto badend; } break; unconditional: // if it's already been mapped, no point going there. index = dest; break; case 0x82: jumpboundary = true; colorInfo[opcodeLocation] = teal; tail = opcodeLocation + 3; dest = tail + (short) ((int) readBytes[1] + ((int) (readBytes[0]) << 8)); goto assignLocations; break; default: colorInfo[opcodeLocation] = 0; //no nossing break; } strcpy(temp,adjustedString); //end if(bytestoread) } if(opcode == 0x60 || opcode == 0x6B) colorInfo[opcodeLocation] = orange, jumpboundary = true; //everything is fine in the case of one byte instructions. //store the semicolon in a char*. char *semicolon = ";"; char working[256]; //copy the adjusted String to another buffer. char holder[256]; strcpy(holder, temp); if(MONSettings) { if(temp[4] == '#') { strncpy(working, temp, 5); for(int i = 6; temp[i] != 0; i++) { working[i-1] = temp[i]; } working[i-1] = 0; // null terminate it. //adjustedString has the $ taken out. strcpy(temp,working); } //make holder one byte longer than adjustedString. strcpy(holder, temp); // add in the semicolon. strcat(holder, semicolon); } //convert to all caps. strupr(holder); //write to the outputBuffer if(opcodeLocation >= 0 && opcodeLocation < bufLength) { outputBuffer[opcodeLocation] = (char*) malloc(strlen(holder)+1); strcpy(outputBuffer[opcodeLocation],holder); } //Occurs if the routine attempts to jump to some unknown location. //The area after the JMP should not be assumed to be code, even if //it turns out to be true. if(jumpboundary) break; } //*************************************** // STEP TWO: CHECK UNRESOLVED OPCODES. // determine if we need to make another pass for(index = 0; index < bufLength; index++) { if((maskBits[index] & 0x01) == 1) if(outputBuffer[index] == 0) goto anotherpass; } //*************************************** // STEP THREE: CHECK FOR UNMARKED BYTES (either inaccessible code, or data.) //checking if there were bytes in the buffer that were not mapped, indicating a split routine, or internal data. //Linear override provides a way to handle routines that are observed to have many different entry points //which are made up of contiguous code, yet look suspicious from the disassemblers point of view. //This is because there is no internal way to reach these location from the data provided. See examples //in the help file (once I make it ;) ) for(index = 0; index < bufLength; index++) { if(maskBits[index] == 0) { if(LinearOverride) { //mark this location as an alternate entry point, since we're doing the whole //linear override thing and all. maskBits[index] |= 0x40; Acc16 = AccPrior; if(Acc16) CheckDlgButton(thisWin, IDC_ACC16, BST_CHECKED); else CheckDlgButton(thisWin, IDC_ACC16, BST_UNCHECKED); XY16 = XYPrior; if(XY16) CheckDlgButton(thisWin, IDC_XY16, BST_CHECKED); else CheckDlgButton(thisWin, IDC_XY16, BST_UNCHECKED); goto anotherpass; } MessageBox(thisWin, "Your routine contains data, reset your bounds so that it doesn't.\nOr choose the linear override option.","error",MB_OK); goto badend; } } LinearOverride = false; CheckDlgButton(thisWin, IDC_LINEAR, BST_UNCHECKED); //reset so we can go through the output loop. index = 0; //*************************************** //STEP FOUR: WRITE THE OUTPUT TO THE FILE. //write the output tofile if(RTF) { WriteRTFHeader(); LnBrk = "\\par\x0D\x0A"; tab = "\\tab "; leftBrack = "\\{"; rightBrack = "\\}"; strcpy(dummy,"\\cf1\\b\\f0\\fs24 "); WriteFile(output,dummy,strlen(dummy), &numBytesWritten, 0); } else { LnBrk = "\x0D\x0A"; tab = "\x09"; leftBrack = "{"; rightBrack = "}"; } //Write out the $xxxx-$yyyy crap. GetDlgItemText(thisWin, IDC_LOWER, rangeBuf, 256); WriteFile(output,"$",1,&numBytesWritten,0); WriteFile(output,rangeBuf,strlen(rangeBuf),&numBytesWritten,0); WriteFile(output,"-$",2,&numBytesWritten,0); GetDlgItemText(thisWin, IDC_UPPER, rangeBuf, 256); WriteFile(output,rangeBuf,strlen(rangeBuf),&numBytesWritten,0); if(RTF) { strcpy(dummy, "\\cf0\\b0"); WriteFile(output,dummy, strlen(dummy), &numBytesWritten, 0); } strcpy(dummy,LnBrk); strcat(dummy, leftBrack); strcat(dummy,LnBrk); WriteFile(output,dummy,strlen(dummy),&numBytesWritten,0); while(index < bufLength + 1) { int temp; if( (maskBits[index] & 0x21) == 0x20) { index++; continue; } //check for alternate entry points first... if( (maskBits[index] & 0x40) == 0x40 ) { strcpy(dummy, LnBrk); strcat(dummy, LnBrk); WriteFile(output,dummy,strlen(dummy) - numNewLines,&numNewLines,0); ZeroMemory(dummy,256); if(RTF) WriteFile(output,colorList[1],strlen(colorList[1]),&numBytesWritten,0); strcpy(dummy, "$"); temp = index + lower; if(SNESAddr) temp = GetSNESAddress(temp); itoa(temp, dummy + 1, 16); strupr(dummy); strncpy(jumpLocation,dummy,strlen(dummy)); strcpy(jumpLocation + strlen(dummy)," ALTERNATE ENTRY POINT"); WriteFile(output, jumpLocation, strlen(jumpLocation), &numBytesWritten, 0); if( (maskBits[index] & 0x02) == 0x02) { if(RTF) { WriteFile(output,colorList[2],strlen(colorList[2]),&numBytesWritten,0); WriteFile(output,"\\b ",3,&numBytesWritten,0); } //make a new line for the branch portion to appear on. WriteFile( output, LnBrk ,strlen(LnBrk), &numNewLines, 0 ); temp = index + lower; if(SNESAddr) { temp = GetSNESAddress(temp); dummy[0] = '$'; itoa(temp, dummy + 1, 16); } else strcpy(dummy, labelList[ branchDest[index] ]); WriteFile(output,dummy,strlen(dummy), &numBytesWritten, 0); if(MONSettings) WriteFile(output,":",1,&numBytesWritten,0); if(RTF) WriteFile(output,"\\b0",3,&numBytesWritten,0); } strcpy(dummy, LnBrk); strcat(dummy, LnBrk); WriteFile(output,dummy,strlen(dummy),&numNewLines,0); } else if( (maskBits[index] & 0x02) == 0x02 ) { if(RTF) { WriteFile(output,colorList[2],strlen(colorList[2]),&numBytesWritten,0); WriteFile(output,"\\b ",3,&numBytesWritten,0); } strcpy(dummy, LnBrk); strcat(dummy, LnBrk); WriteFile(output,dummy,strlen(dummy) - numNewLines,&numNewLines,0); temp = index + lower; if(SNESAddr) { temp = GetSNESAddress(temp); dummy[0] = '$'; itoa(temp, dummy + 1, 16); } else strcpy(dummy, labelList[ branchDest[index] ]); WriteFile(output,dummy,strlen(dummy), &numBytesWritten, 0); if(MONSettings) WriteFile(output,":",1,&numBytesWritten,0); if(RTF) WriteFile(output,"\\b0",3,&numBytesWritten,0); strcpy(dummy, LnBrk); strcat(dummy, LnBrk); WriteFile(output,dummy,strlen(dummy),&numNewLines,0); } //if it's an opcode... if( (maskBits[index] & 0x01) == 0x01) { if(outputBuffer[index] == 0) { MessageBox(thisWin, "Opcode has no output information!","error",MB_OK); WriteFile(output,"Error",5,&numBytesWritten, 0); //move on for now to the next byte. index++; continue; } // if it happens to be a branch point, fill in the necessary strings if( (maskBits[index] & 0x04) == 0x04 ) { if(RTF) { strcpy(dummy,colorList[colorInfo[index]]); strcat(dummy,"\\b "); WriteFile(output,dummy,strlen(dummy),&numBytesWritten,0); } if( numNewLines == strlen(LnBrk) ) WriteFile( output, LnBrk ,strlen(LnBrk), &numNewLines, 0 ); WriteFile(output,tab,strlen(tab),&numBytesWritten,0); if(!SNESAddr) //get the branch label (BRANCH_ALPHA, etc...) strcpy( dummy, labelList[ branchPoints[index] ] ); if(SNESAddr) { for(int k = 0; k < bufLength + 1; k++) { if(branchDest[k] == branchPoints[index]) break; } strcpy(dummy, "BRANCH_$"); temp = GetSNESAddress(k + index); itoa(temp, dummy + strlen(dummy), 16); strupr(dummy); } //write the first four bytes, then apply the label. WriteFile(output, outputBuffer[index], 4, &numBytesWritten, 0); WriteFile(output, dummy, strlen(dummy), &numBytesWritten, 0); if(MONSettings) WriteFile(output,";",1,&numBytesWritten,0); if(RTF) //keep it from being bold. WriteFile(output, "\\b0",3,&numBytesWritten,0); strcpy(dummy, LnBrk); strcat(dummy, LnBrk); WriteFile(output,dummy,strlen(dummy),&numNewLines,0); } else { //just a normal opcode, not a branch point. if(RTF) { strcpy(dummy,colorList[colorInfo[index]]); if(colorInfo[index] > 0) strcat(dummy,"\\b "); WriteFile(output,dummy,strlen(dummy),&numBytesWritten,0); } //print a tab. WriteFile(output,tab,strlen(tab),&numBytesWritten,0); //print the opcode string WriteFile(output,outputBuffer[index],strlen(outputBuffer[index]),&numBytesWritten,0); if(colorInfo[index] > 0) if(RTF) WriteFile(output, "\\b0",3,&numBytesWritten,0); //print a line break. WriteFile( output, LnBrk ,strlen(LnBrk), &numNewLines, 0); } } index++; } if(RTF) { strcpy(dummy,"\\cf0\\}\0x00"); WriteFile(output,dummy,strlen(dummy)+1,&numBytesWritten,0); } else WriteFile(output, rightBrack,strlen(rightBrack),&numBytesWritten, 0); MessageBox(thisWin,"Disassembly Completed","success",MB_OK); badend: LinearOverride = false; CheckDlgButton(thisWin, IDC_LINEAR, BST_UNCHECKED); CloseHandle(output); free(maskBits); free(branchPoints); free(branchDest); free(outputBuffer); free(colorInfo); Acc16 = AccPrior; if(Acc16) CheckDlgButton(thisWin, IDC_ACC16, BST_CHECKED); else CheckDlgButton(thisWin, IDC_ACC16, BST_UNCHECKED); XY16 = XYPrior; if(XY16) CheckDlgButton(thisWin, IDC_XY16, BST_CHECKED); else CheckDlgButton(thisWin, IDC_XY16, BST_UNCHECKED); return 1; } int Disassemble(HWND win, int lower, int upper, int bufLength) { if(IsDlgButtonChecked(win, IDC_COLOR) == BST_CHECKED) RTF = true; else RTF = false; int temp = bufLength; char *dummy = "output.txt"; if(!JumpTable) { if(RTF) dummy = "output.log"; } else { if(RTF) dummy = "jumptable.log"; else dummy = "jumptable.txt"; } //user in the Write/ReadFile() routines unsigned long numBytesRead; //a buffer to store the romdata we wanted. char *romBuffer; //If there's a header, offset our numbers by $200 bytes. if(Header) lower += 0x200, upper += 0x200; //try to assign the output handle. if(!(output = CreateFile(dummy, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0))) { MessageBox(win, "File Creation failed", "whoops", MB_OK); return 0; } //set the point to begin reading. SetFilePointer(file, lower, 0, 0); //allocate romBuffer = (char*) malloc(bufLength); if(!ReadFile(file, romBuffer, bufLength, &numBytesRead, 0)) { int p = GetLastError(); MessageBox(win, "we f'ed up", "don't know why", MB_OK); return 0; } /* used for testing purposes if(!WriteFile(output, romBuffer, bufLength, &numBytesRead, 0)) { int p = GetLastError(); MessageBox(win, "write failed", "don't know why", MB_OK); } */ if(!JumpTable) DisasmLoop(romBuffer, bufLength, lower); else GenerateJumpTable(romBuffer, bufLength, lower); CloseHandle(output); free(romBuffer); return 1; } int GetArguments(HWND win, int *lower, int *upper) { int i; char hextemp[256]; char buffer[256]; if(!GetDlgItemText(win, IDC_LOWER, hextemp, 256)) { MessageBox(win, "there is no number in the lower limit box", "please do so", MB_OK); return 0; } for(i = 0; hextemp[i] != 0; i++) { int j = hextemp[i]; if(j >= 'A' && j <= 'F') continue; if(j >= '0' && j <= '9') continue; else { MessageBox(win, "Edit box contains non-hex characters", "you naughty boy", MB_OK); return 0; } } *lower = hextodec(hextemp, i); itoa(*lower, buffer, 10); //MessageBox(win,buffer,"you naughty boy", MB_OK); if(!GetDlgItemText(win, IDC_UPPER, hextemp, 256)) { MessageBox(win, "there is no number in the upper limit box", "finish what you start", MB_OK); return 0; } for(i = 0; hextemp[i] != 0; i++) { int j = hextemp[i]; if(j >= 'A' && j <= 'F') continue; if(j >= '0' && j <= '9') continue; else { MessageBox(win, "Edit box contains non-hex characters", "you naughty boy", MB_OK); return 0; } } *upper = hextodec(hextemp, i); itoa(*upper, buffer, 10); //MessageBox(win,buffer,"you naughty boy", MB_OK); return 1; } int GetFile(OPENFILENAME *ofn, HWND win) { ofn->lStructSize = sizeof(*ofn); ofn->hwndOwner = win; ofn->hInstance = thisOne; ofn->lpstrFilter = "SNES roms\0*.SMC;*.SFC\0All files\0*.*\0"; ofn->lpstrCustomFilter = 0; ofn->nFilterIndex = 1; ofn->nMaxFile = MAX_PATH; ofn->lpstrFile = filename; filename[0] = 0; ofn->lpstrFileTitle = NULL; ofn->lpstrInitialDir = 0; ofn->lpstrTitle = "Open game"; ofn->Flags = OFN_FILEMUSTEXIST; ofn->lpfnHook=0; if(!GetOpenFileName(ofn)) { char* buffer = 0; DWORD i=CommDlgExtendedError(); if(i) { wsprintf(buffer,"GUI error. Error: %08X",i); MessageBox(0,buffer,"Bad error happened",MB_OK); return 0; } } else { // this releases the old file when we switch files, for example. if(file) CloseHandle(file); file = CreateFile(ofn-> lpstrFile ,GENERIC_READ ,FILE_SHARE_READ ,0 ,OPEN_EXISTING ,FILE_ATTRIBUTE_NORMAL ,(HANDLE) NULL); } return 1; } int CALLBACK disasmDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { int lower; int upper; int j; int k; char *itemname = "File"; char outputBuf[256]; JumpTable = false; switch(msg) { case WM_INITDIALOG: thisWin = win; SetMenu(thisWin, thisMenu); DrawMenuBar(thisWin); SetDlgItemText(win, IDC_STATIC5, (LPSTR) "Initial Settings"); CheckDlgButton(win, IDC_ALERT, BST_CHECKED); CheckMenuItem(thisMenu, IDC_JUMP1, MF_CHECKED); GetSettings(); break; case WM_ACTIVATE: ShowWindow(win, SW_SHOW); Visible = true; break; case WM_KEYDOWN: if(lparam & 0x40000000) break; switch(wparam) { case VK_F1: ShowWindow(win, SW_MINIMIZE); break; case VK_F2: SendMessage(thisWin, WM_COMMAND, IDC_OPEN, 0); break; case VK_F3: SendMessage(thisWin, WM_COMMAND, IDOK, 0); break; case VK_F4: SendMessage(thisWin, WM_COMMAND, IDC_JUMPTABLE, 0); break; case VK_F5: LinearOverride = true; SendMessage(thisWin, WM_COMMAND, IDOK, 0); break; case VK_F6: DialogBoxParam(thisOne, MAKEINTRESOURCE(IDD_DIALOG2), thisWin, rareDlgProc, 0); break; case VK_F7: noErrors = !noErrors; break; } break; case WM_COMMAND: switch(wparam) { case IDC_JUMP1: case IDC_JUMP2: case IDC_JUMP3: case IDC_JUMP4: for(j = 0; j < 4; j++) { k = jumpTableTypes[j]; if( wparam == (unsigned) jumpTableTypes[j]) { jumpSelector = j; CheckMenuItem(thisMenu, jumpTableTypes[j], MF_CHECKED); } else CheckMenuItem(thisMenu, jumpTableTypes[j], MF_UNCHECKED); } break; case IDC_JUMPTABLE: JumpTable = true; case IDOK: if(!file) { MessageBox(win, "No file has been loaded", "load one plz", MB_OK); break; } if(GetArguments(win, &lower, &upper)) { if(lower > upper) { MessageBox(win, "Your bounds are out of whack", "stfu foo", MB_OK); break; } if(upper > filesize) { MessageBox(win, "Your upperbound exceeds the size of the file", "tsk tsk", MB_OK); break; } Disassemble(win, lower, upper, upper - lower + 1); break; } break; case IDC_STYLE: MONSettings = !MONSettings; if(MONSettings) { SetDlgItemText(win, IDC_STYLE, "MathOnNapkins Settings -toggle-"); CheckMenuItem(thisMenu, IDC_STYLE, MF_CHECKED); } else { SetDlgItemText(win, IDC_STYLE, "Normal Settings -toggle-"); CheckMenuItem(thisMenu, IDC_STYLE, MF_UNCHECKED); } break; case IDC_OPEN: if(GetFile(&openfn, win)) { SetDlgItemText(win, IDC_FILENAME, (LPSTR) filename); filesize = GetFileSize(file, NULL); if(IsDlgButtonChecked(win, IDC_CHECKHEADER) == BST_CHECKED) { if(filesize > 0x200) { SetFilePointer(file, 0x200, 0, 0); filesize -= 0x200; } else { MessageBox(win, "File is too small to have a header", "Header error", MB_OK); CheckDlgButton(win, IDC_CHECKHEADER, BST_UNCHECKED); } } itoa(filesize, outputBuf, 16); SetDlgItemText(win, IDC_FILESIZE, outputBuf); } break; case IDCANCEL: if(MessageBox(win,"Are you sure you want to quit?","Exit?",MB_YESNO) == IDNO) break; if(file) CloseHandle(file); SaveSettings(); EndDialog(win, -1); thisWin = NULL; break; } break; default: // Handle associated boolean values for checkboxes. if(IsDlgButtonChecked(win, IDC_ALERT) == BST_CHECKED) { if(!Alert) Alert = !Alert; } else if(Alert) Alert = !Alert; //designed to react to user input in the checkboxes //If the header option is not set, //and the file is valid if((!Header) && file) { //but the dialog button is checked, try to apply the header if(IsDlgButtonChecked(win, IDC_CHECKHEADER) == BST_CHECKED) { if(filesize > 0x200) { SetFilePointer(file, 0x200, 0, 0); filesize -= 0x200; Header = true; itoa(filesize, outputBuf, 16); SetDlgItemText(win, IDC_FILESIZE, outputBuf); } else { //else, the file shouldn't be able to have a header //uncheck the box, tell the user. //MessageBox(win, "File is too small to have a header", "Header error", MB_OK); CheckDlgButton(win, IDC_CHECKHEADER, BST_UNCHECKED); } } break; } //If the Header is applied, and the file is valid. if(Header && file) { //but the checkbox says to stop using the header... if(IsDlgButtonChecked(win, IDC_CHECKHEADER) == BST_UNCHECKED) { SetFilePointer(file, 0, 0, 0); filesize = GetFileSize(file, 0); Header = false; itoa(filesize, outputBuf, 16); SetDlgItemText(win, IDC_FILESIZE, outputBuf); } } break; } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG disMsg; jumpSelector = 0; thisMenu = LoadMenu( hInstance, MAKEINTRESOURCE( IDR_MENU1 ) ); // this is the handle to this program. thisOne = hInstance; // create out structured array of strings for output. AssignOps(); AssignLabels(); InitializeRareOpcodes(); thisWin = CreateDialog(thisOne, MAKEINTRESOURCE(IDD_DIALOG1), 0, disasmDlgProc); while(1) { if(thisWin == NULL) break; GetMessage(&disMsg, thisWin, 0, 0); if(disMsg.message == WM_KEYDOWN) disasmDlgProc(thisWin, disMsg.message, disMsg.wParam, disMsg.lParam); IsDialogMessage(thisWin, &disMsg); } return 0; }