#include // // This file defines SFR names and bit names for Microchip's PIC processors. // // This version is for 16F84 , 68 bytes of ram 0x0c to 0x4f inc. // Other PIC16 series should be easy to implement // Should just be a matter of extending the address ranges // and adding the appropriate Register definitions / Enums ( for bitmapped registers ) // How to use this module: // Create a stripped pic.cfg containing the barest minimum , get rid // of all registers that aren't used in your platform // in fact you can wipe ALL registers defined in pic.cfg , because this // script will redefine them. // Just load your hex file ( such as intended for Picstart/Picmaster programers // Set processor type to PIC16 // Let IDA do it's stuff // Run this script // Warning , the first thing this script does is delete the database // So - don't create anything BEFORE running the script - you'll lose it :-) // Still to be done // Improve comments ( of PIC registers/bitnames ) static DB_Reg(off,name,comment) { auto x; x=[SegByName("DATA"),off]; // Only create a name if one doesn't already exist if (LocByName(name)==-1) { MakeByte(x); MakeName(x,name); MakeRptCmt(x,name); } } static Enums(id,number,name,comment) { AddConstEx(id,name,number,-1); SetConstCmt(GetConst(id,number,-1),comment,1); } static main() { auto id; auto a; auto b; auto c; auto d; auto e; auto f; auto g; AutoShow(0); // Stop IDA trying to analyse just yet DeleteAll(); // Clear everything out SegCreate(0,0xff,0x0000,0x0,saRelByte,scPriv); // Create the RAM segment SegClass(0,"DATA"); // Give it a CLASS SegRename(0,"DATA"); // Give it a name SegCreate(0x10000,0x103FF,0x1000,0x0,saRelByte,scPriv); SegClass(0x10000,"CODE"); SegRename(0x10000,"CODE"); // Create the Configuration register segment for 16F84 , actually this exists at this // location for all pic chips ( I think !) SegCreate(0x12000,0x1200F,0x1200,0x0,saRelByte,scPriv); SegClass(0x12000,"CONFIG"); SegRename(0x12000,"CONFIG"); // Define a segment for the internal EEPROM in the 16C84/F84 chips SegCreate(0x12100,0x12140,0x1210,0x0,saRelByte,scPriv); SegClass(0x12100,"EEPROM"); SegRename(0x12100,"EEPROM"); // Well , as we cleared the DB - and didn't want IDA to name the locations // we'll do it here , ideally you should create a pic.cfg with NO definitions DB_Reg(0x00,"INDF","; Indirect access Register"); DB_Reg(0x01,"TMR0","; Timer 0 register"); DB_Reg(0x02,"PCL",""); DB_Reg(0x03,"STATUS",""); // Create some enumerations , I'm not sure this is the way to do this - but it works. // BTW - it would be nice if IDA gave some indication of exactly WHICH enumeration // the constant comes from - when you right click on an operand. // N.B. always create the enum which the name of the associated register // prefixed with the letter e , this allows the later code to automatically // select the correct enum. id=AddEnum(-1,"eSTATUS",0x1100000); Enums(id,7,"IRP","; register bank select (indirect)"); Enums(id,6,"RP1","; register bank select1 (direct)"); Enums(id,5,"RP0","; register bank select0 (direct)"); Enums(id,4,"NOT_T0",""); Enums(id,3,"NOT_PD",""); Enums(id,2,"Z",""); Enums(id,1,"DC",""); Enums(id,0,"C",""); DB_Reg(0x04,"FSR",""); DB_Reg(0x05,"PORTA","; Port A data register"); DB_Reg(0x06,"PORTB","; Port B data register"); DB_Reg(0x08,"EEDATA","; EEPROM Data Register"); DB_Reg(0x09,"EEADR","; EEPROM Address Register"); DB_Reg(0x0A,"PCLATH",""); // Some of these definitions are just here for completeness DB_Reg(0x0B,"INTCON",""); // The DB_Reg routine won't create them if they already exist. id=AddEnum(-1,"eINTCON",0x1100000); Enums(id,7,"GIE","; General Interrupt enable"); Enums(id,5,"T0IE","; Timer 0 interrupt enable"); Enums(id,4,"INTE","; "); Enums(id,3,"RBIE","; "); Enums(id,2,"T0IF","; "); Enums(id,1,"INTF","; "); Enums(id,0,"RBIF","; "); DB_Reg(0x80,"INDF",""); DB_Reg(0x81,"OPTION_REG",""); id=AddEnum(-1,"eOPTION_REG",0x1100000); Enums(id,7,"NOT_RBPU",""); Enums(id,6,"INTEDG",""); Enums(id,5,"T0CS",""); Enums(id,4,"T0SE",""); Enums(id,3,"PSA",""); Enums(id,2,"PS2",""); Enums(id,1,"PS1",""); Enums(id,0,"PS0",""); DB_Reg(0x82,"PCL",""); DB_Reg(0x83,"STATUS","; Processor Status Register"); DB_Reg(0x84,"FSR",""); DB_Reg(0x85,"TRISA","; Port A Tri-State Control Register"); DB_Reg(0x86,"TRISB","; Port B Tri-State Control Register"); DB_Reg(0x88,"EECON1","; EEPROM Control Register"); id=AddEnum(-1,"eEECON1",0x1100000); Enums(id,4,"EEIF",""); Enums(id,3,"WRERR",""); Enums(id,2,"WREN",""); Enums(id,1,"WR",""); Enums(id,0,"RD",""); DB_Reg(0x89,"EECON2","; EEPROM Write access Register"); DB_Reg(0x8A,"PCLATH",""); DB_Reg(0x8B,"INTCON",""); AutoShow(1); Wait(); // Wait for end of analysis // This is just a test bit of code to see if we can iterate through the // disassembly looking for likely enumerations , because the pic modules definations // have been scrapped , we'll do it here in the IDC language // Ideally the parameters for this will be read in from a slightly better pic.cfg file. // Also we do a couple of no-brainer conversions (tris/FSR) // Just a little informative messaging Message("Starting ENUM convert\n"); //Give some idea of what range we're covering Message("Start at %08lX\n",SegStart(SegByName("CODE"))); Message("End at %08lX\n",SegEnd(SegByName("CODE"))); // No real point doing this any other way , all instructions are nicely one 'element' // in size for the PIC , so we might as well cover the complete range // no need to concern ourselves with data bytes - there aren't any in code space // the onlything like a data byte is a retlw nn. for (a=SegStart(SegByName("CODE"));a<=SegEnd(SegByName("CODE"));a=a+1) { c=GetOperandValue(a,0); b=GetOperandValue(a,1); d=GetMnem(a); // GetOperandValue is slightly broke for PIC // Only ever seems to supply one operand // e.g. bsf EECON1,4 doesn't correctly report operand0 as the value for EECON1 // but movlw 0x22 does return 0x22 in operand 0 - weird. // HOWEVER GetOpnd does correctly return the string EECON1 !!! //Message("%08lX: %s op1= %08lX(%s) op2=%08lX(%s) \n",a,d,c,GetOpnd(a,0),b,GetOpnd(a,1)); // Check for tris if (d=="tris") { OpOff(a,0,SegStart(SegByName("DATA"))); // Convert to an offset in DATA seg // If the previous instruction is a movlw // it's better to represent it as binary // then you can see which bits are input/output if (GetMnem(a-1)=="movlw") OpBinary(a-1,0); } // Check for a movlw preceeding a movwf FSR , convert the movlw operand to offset if (d=="movwf") { if (GetOpnd(a,0)=="FSR") { if (GetMnem(a-1)=="movlw") OpOff(a-1,0,SegStart(SegByName("DATA"))); } } // Do the enums for bit mapped registers f=GetOpnd(a,0);f="e"+f; // Fake up Enum for this register g=GetEnum(f); // Get the id for the named Enum if ((g!=-1) && (b!=-1)) OpEnum(a,1,g); // If the enum is valid // and the second operand exists // then convert the operand to this enum } }