mirror of https://github.com/wwarthen/RomWBW.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
992 lines
27 KiB
992 lines
27 KiB
|
|
///////////////////////////////////////////////////////////////
|
|
// //
|
|
// Propeller Spin/PASM Compiler Command Line Tool 'OpenSpin' //
|
|
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
|
|
// See end of file for terms of use. //
|
|
// //
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// UnusedMethodUtils.cpp
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "PropellerCompiler.h"
|
|
|
|
//
|
|
// track object names based on "indent" or which child/parent level
|
|
// note: the same name can be in here multiple times at different indent levels
|
|
//
|
|
|
|
struct ObjectNameEntry
|
|
{
|
|
char filename[256];
|
|
int nCompileIndex;
|
|
};
|
|
|
|
ObjectNameEntry s_objectNames[file_limit * file_limit];
|
|
int s_nNumObjectNames = 0;
|
|
|
|
void AddObjectName(char* pFilename, int nCompileIndex)
|
|
{
|
|
strcpy(s_objectNames[s_nNumObjectNames].filename, pFilename);
|
|
|
|
// chop off the .spin extension
|
|
char* pExtension = strstr(s_objectNames[s_nNumObjectNames].filename, ".spin");
|
|
if (pExtension != 0)
|
|
{
|
|
*pExtension = 0;
|
|
}
|
|
|
|
s_objectNames[s_nNumObjectNames].nCompileIndex = nCompileIndex;
|
|
s_nNumObjectNames++;
|
|
}
|
|
|
|
int GetObjectName(int nCompileIndex)
|
|
{
|
|
for (int i = 0; i < s_nNumObjectNames; i++)
|
|
{
|
|
if (s_objectNames[i].nCompileIndex == nCompileIndex)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// track method usage by object
|
|
//
|
|
|
|
struct IndexEntry
|
|
{
|
|
short offset; // offset in longs to method (or sub object)
|
|
short vars; // var offset for objs, locals size for methods
|
|
};
|
|
|
|
struct CallEntry
|
|
{
|
|
unsigned char* objaddress;
|
|
unsigned int callOffset;
|
|
unsigned short objoffset;
|
|
unsigned char opcode;
|
|
unsigned char pubnum;
|
|
unsigned char objnum;
|
|
};
|
|
|
|
struct MethodUsage
|
|
{
|
|
int nLength;
|
|
int nCalled;
|
|
int nCalls;
|
|
CallEntry *pCalls;
|
|
int nCurrCall;
|
|
int nNewIndex;
|
|
};
|
|
|
|
struct ObjectEntry
|
|
{
|
|
int nObjectNameIndex;
|
|
unsigned char* pObject;
|
|
int nObjectMethodCount;
|
|
int nObjectSubObjectCount;
|
|
int nObjectIndexCount;
|
|
int nMethodsCalled;
|
|
int nNewObjectIndex;
|
|
IndexEntry* pIndexTable;
|
|
MethodUsage* pMethods;
|
|
};
|
|
|
|
ObjectEntry s_objects[file_limit * file_limit];
|
|
int s_nNumObjects;
|
|
|
|
bool HaveObject(unsigned char* pObject)
|
|
{
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
if (s_objects[i].pObject == pObject)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ObjectEntry* GetObject(unsigned char* pObject)
|
|
{
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
if (s_objects[i].pObject == pObject)
|
|
{
|
|
return &s_objects[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ObjectEntry* GetObjectByName(char* pFilename)
|
|
{
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
if (strcmp(s_objectNames[s_objects[i].nObjectNameIndex].filename, pFilename) == 0)
|
|
{
|
|
return &s_objects[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int AddObject(unsigned char* pObject, int nObjectNameIndex)
|
|
{
|
|
s_objects[s_nNumObjects].pObject = pObject;
|
|
s_objects[s_nNumObjects].nObjectNameIndex = nObjectNameIndex;
|
|
s_objects[s_nNumObjects].nObjectMethodCount = pObject[2]-1;
|
|
s_objects[s_nNumObjects].nObjectSubObjectCount = pObject[3];
|
|
s_objects[s_nNumObjects].nObjectIndexCount = s_objects[s_nNumObjects].nObjectMethodCount + s_objects[s_nNumObjects].nObjectSubObjectCount;
|
|
s_objects[s_nNumObjects].pIndexTable = (IndexEntry *)&(pObject[4]);
|
|
s_objects[s_nNumObjects].pMethods = new MethodUsage[s_objects[s_nNumObjects].nObjectMethodCount];
|
|
for (int i = 0; i < s_objects[s_nNumObjects].nObjectMethodCount; i++)
|
|
{
|
|
s_objects[s_nNumObjects].pMethods[i].nCalled = 0;
|
|
s_objects[s_nNumObjects].pMethods[i].nCalls = 0;
|
|
s_objects[s_nNumObjects].pMethods[i].pCalls = 0;
|
|
s_objects[s_nNumObjects].pMethods[i].nCurrCall = 0;
|
|
s_objects[s_nNumObjects].pMethods[i].nNewIndex = 0;
|
|
s_objects[s_nNumObjects].pMethods[i].nLength = 0;
|
|
}
|
|
return s_nNumObjects++;
|
|
}
|
|
|
|
bool IsObjectUsed(char* pFilename)
|
|
{
|
|
// chop off the .spin extension, saving the . char for restoring
|
|
char* pExtension = strstr(pFilename, ".spin");
|
|
char savedChar = 0;
|
|
if (pExtension != 0)
|
|
{
|
|
savedChar = *pExtension;
|
|
*pExtension = 0;
|
|
}
|
|
|
|
ObjectEntry* pObject = GetObjectByName(pFilename);
|
|
|
|
// restore extention to passed in filename
|
|
if (pExtension != 0)
|
|
{
|
|
*pExtension = savedChar;
|
|
}
|
|
|
|
if (pObject && pObject->nMethodsCalled > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IsMethodUsed(char* pFilename, int nMethod)
|
|
{
|
|
ObjectEntry* pObject = GetObjectByName(pFilename);
|
|
if (pObject && pObject->nMethodsCalled > 0 && pObject->pMethods[nMethod].nCalled > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// store pubcon list data so it can be used in the final compile
|
|
// note: this is needed to allow removing a child object where the parent used only CONs from the child
|
|
//
|
|
|
|
struct ObjectPubConListEntry
|
|
{
|
|
char filename[256];
|
|
unsigned char* pPubConList;
|
|
int nPubConListSize;
|
|
};
|
|
|
|
ObjectPubConListEntry s_objectPubConLists[file_limit * file_limit];
|
|
int s_nNumObjectPubConLists;
|
|
|
|
ObjectPubConListEntry* GetObjectPubConListEntryByName(char* pFilename)
|
|
{
|
|
for (int i = 0; i < s_nNumObjectPubConLists; i++)
|
|
{
|
|
if (strcmp(s_objectPubConLists[i].filename, pFilename) == 0)
|
|
{
|
|
return &s_objectPubConLists[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void AddObjectPubConList(char* pFilename, unsigned char* pPubConList, int nPubConListSize)
|
|
{
|
|
strcpy(s_objectPubConLists[s_nNumObjectPubConLists].filename, pFilename);
|
|
s_objectPubConLists[s_nNumObjectPubConLists].pPubConList = new unsigned char[nPubConListSize];
|
|
s_objectPubConLists[s_nNumObjectPubConLists].nPubConListSize = nPubConListSize;
|
|
memcpy(s_objectPubConLists[s_nNumObjectPubConLists].pPubConList, pPubConList, s_objectPubConLists[s_nNumObjectPubConLists].nPubConListSize);
|
|
s_nNumObjectPubConLists++;
|
|
}
|
|
|
|
bool GetObjectPubConList(char* pFilename, unsigned char** ppPubConList, int* pnPubConListSize)
|
|
{
|
|
ObjectPubConListEntry* pObject = GetObjectPubConListEntryByName(pFilename);
|
|
if (pObject && pObject->pPubConList != 0 && pObject->nPubConListSize > 0)
|
|
{
|
|
*ppPubConList = pObject->pPubConList;
|
|
*pnPubConListSize = pObject->nPubConListSize;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct ObjectCogInitEntry
|
|
{
|
|
char filename[256];
|
|
int nSubConstant;
|
|
};
|
|
|
|
ObjectCogInitEntry s_objectCogInits[file_limit * file_limit];
|
|
int s_nNumObjectCogInits;
|
|
|
|
void AddCogNewOrInit(char* pFilename, int nSubConstant)
|
|
{
|
|
if (s_nNumObjectCogInits > 0)
|
|
{
|
|
// see if this combo already is in the array
|
|
for (int i = s_nNumObjectCogInits; i > 0; i--)
|
|
{
|
|
if (s_objectCogInits[i-1].nSubConstant == nSubConstant && strcmp(s_objectCogInits[i-1].filename, pFilename) == 0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// wasn't already there, so add it
|
|
strcpy(s_objectCogInits[s_nNumObjectCogInits].filename, pFilename);
|
|
s_objectCogInits[s_nNumObjectCogInits].nSubConstant = nSubConstant;
|
|
s_nNumObjectCogInits++;
|
|
}
|
|
|
|
void MarkCalls(MethodUsage* pMethod, ObjectEntry* pObject);
|
|
|
|
void CheckForCogNewOrInit(ObjectEntry* pObject)
|
|
{
|
|
char* pObjectFilename = s_objectNames[pObject->nObjectNameIndex].filename;
|
|
for (int i = 0; i < s_nNumObjectCogInits; i++)
|
|
{
|
|
if (strcmp(s_objectCogInits[i].filename, pObjectFilename) == 0)
|
|
{
|
|
// don't do this if the object has no called methods already
|
|
// in that case it means the cognew/coginit is never done, so it's safe to not mark the referred to method
|
|
if (pObject->nMethodsCalled > 0)
|
|
{
|
|
MarkCalls(&(pObject->pMethods[s_objectCogInits[i].nSubConstant - 1]), pObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CleanUpUnusedMethodData()
|
|
{
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
s_objects[i].pObject = 0;
|
|
s_objects[i].pIndexTable = 0;
|
|
|
|
for (int j = 0; j < s_objects[s_nNumObjects].nObjectMethodCount; j++)
|
|
{
|
|
if (s_objects[i].pMethods[j].pCalls)
|
|
{
|
|
delete [] s_objects[i].pMethods[j].pCalls;
|
|
s_objects[i].pMethods[j].pCalls = 0;
|
|
}
|
|
}
|
|
delete [] s_objects[i].pMethods;
|
|
s_objects[i].pMethods = 0;
|
|
}
|
|
s_nNumObjects = 0;
|
|
s_nNumObjectNames = 0;
|
|
|
|
for (int i = 0; i < s_nNumObjectPubConLists; i++)
|
|
{
|
|
delete [] s_objectPubConLists[i].pPubConList;
|
|
s_objectPubConLists[i].pPubConList = 0;
|
|
}
|
|
s_nNumObjectPubConLists = 0;
|
|
|
|
s_nNumObjectCogInits = 0;
|
|
}
|
|
|
|
void InitUnusedMethodData()
|
|
{
|
|
for (int i = 0; i < (file_limit * file_limit); i++)
|
|
{
|
|
s_objectPubConLists[i].filename[0] = 0;
|
|
s_objectPubConLists[i].pPubConList = 0;
|
|
s_objectPubConLists[i].nPubConListSize = 0;
|
|
}
|
|
s_nNumObjectPubConLists = 0;
|
|
s_nNumObjectCogInits = 0;
|
|
s_nNumObjectNames = 0;
|
|
}
|
|
|
|
void AdvanceCompileIndex(unsigned char* pObject, int& nCompileIndex)
|
|
{
|
|
nCompileIndex++;
|
|
|
|
int nNextObjOffset = *((unsigned short *)pObject);
|
|
ObjectEntry* pObjectEntry = GetObject(pObject);
|
|
for (int i = 0; i < pObjectEntry->nObjectIndexCount; i++)
|
|
{
|
|
if (pObjectEntry->pIndexTable[i].offset >= nNextObjOffset)
|
|
{
|
|
AdvanceCompileIndex(&(pObject[pObjectEntry->pIndexTable[i].offset]), nCompileIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildTables(unsigned char* pObject, int indent, int& nCompileIndex)
|
|
{
|
|
#ifdef RPE_DEBUG
|
|
#define MAX_INDENT 32
|
|
char s_indent[MAX_INDENT+1] = " ";
|
|
#endif
|
|
|
|
if (HaveObject(pObject))
|
|
{
|
|
#ifdef RPE_DEBUG
|
|
printf("%sObject Already Added\n", &s_indent[MAX_INDENT-indent]);
|
|
#endif
|
|
AdvanceCompileIndex(pObject, nCompileIndex);
|
|
return;
|
|
}
|
|
nCompileIndex++;
|
|
int nNextObjOffset = *((unsigned short *)pObject);
|
|
int nObjectName = GetObjectName(nCompileIndex);
|
|
int nObject = AddObject(pObject, nObjectName);
|
|
|
|
#ifdef RPE_DEBUG
|
|
printf("%sObject Index Table: %s\n", &s_indent[MAX_INDENT-indent], s_objectNames[s_objects[nObject].nObjectNameIndex].filename);
|
|
#endif
|
|
for (int i = 0; i < s_objects[nObject].nObjectIndexCount; i++)
|
|
{
|
|
if (s_objects[nObject].pIndexTable[i].offset >= nNextObjOffset)
|
|
{
|
|
#ifdef RPE_DEBUG
|
|
printf("%s Object Offset: %04d Vars Offset: %d\n", &s_indent[MAX_INDENT-indent], s_objects[nObject].pIndexTable[i].offset, s_objects[nObject].pIndexTable[i].vars);
|
|
#endif
|
|
// this skip logic here is to handle the case where there are multiple instances of the same object source included
|
|
// either as an array of objects or as separately named objects
|
|
bool bSkip = false;
|
|
for (int j = 0; j < i; j++)
|
|
{
|
|
if (s_objects[nObject].pIndexTable[i].offset == s_objects[nObject].pIndexTable[j].offset)
|
|
{
|
|
bSkip = true;
|
|
}
|
|
}
|
|
if (!bSkip)
|
|
{
|
|
BuildTables(&(pObject[s_objects[nObject].pIndexTable[i].offset]), indent + 1, nCompileIndex);
|
|
}
|
|
}
|
|
#ifdef RPE_DEBUG
|
|
else
|
|
{
|
|
printf("%s Method Offset: %04d Locals size: %d\n", &s_indent[MAX_INDENT-indent], s_objects[nObject].pIndexTable[i].offset, s_objects[nObject].pIndexTable[i].vars);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// byte code scanning stuff
|
|
// borrowed from Dave Hein's spinsim code and then modified for my needs (mostly stripped down to just skip intelligently over byte code)
|
|
//
|
|
|
|
int SkipSignedOffset(unsigned char* pOpcode)
|
|
{
|
|
return (*pOpcode < 0x80) ? 1 : 2;
|
|
}
|
|
|
|
int SkipUnsignedOffset(unsigned char* pOpcode)
|
|
{
|
|
return (*pOpcode & 0x80) ? 2 : 1;
|
|
}
|
|
|
|
int ScanMathOpcode(unsigned char* pOpcode)
|
|
{
|
|
bool execflag = false;
|
|
int opcode = *pOpcode;
|
|
|
|
if (opcode < 0xe0)
|
|
{
|
|
execflag = true;
|
|
opcode += 0xe0 - 0x40;
|
|
}
|
|
|
|
// Execute the math op
|
|
switch (opcode)
|
|
{
|
|
case 0xe0: // ror
|
|
case 0xe1: // rol
|
|
case 0xe2: // shr
|
|
case 0xe3: // shl
|
|
case 0xe4: // min
|
|
case 0xe5: // max
|
|
case 0xe6: // neg
|
|
case 0xe7: // com
|
|
case 0xe8: // and
|
|
case 0xe9: // abs
|
|
case 0xea: // or
|
|
case 0xeb: // xor
|
|
case 0xec: // add
|
|
case 0xed: // sub
|
|
case 0xee: // sar
|
|
case 0xef: // rev
|
|
case 0xf0: // andl
|
|
case 0xf1: // encode
|
|
case 0xf4: // mul
|
|
case 0xf5: // mulh
|
|
case 0xf2: // orl
|
|
case 0xf3: // decode
|
|
case 0xf6: // div
|
|
case 0xf7: // mod
|
|
case 0xf8: // sqrt
|
|
case 0xf9: // cmplt
|
|
case 0xfa: // cmpgt
|
|
case 0xfb: // cmpne
|
|
case 0xfc: // cmpeq
|
|
case 0xfd: // cmple
|
|
case 0xfe: // cmpgr
|
|
case 0xff: // notl
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (execflag ? 0 : 1);
|
|
}
|
|
|
|
int ScanExtraOpcode(unsigned char* pOpcode, int opcode)
|
|
{
|
|
int nOpSize = 0;
|
|
|
|
opcode &= 0x7f;
|
|
|
|
if (opcode >= 0x40 && opcode < 0x60) // math op
|
|
{
|
|
nOpSize += ScanMathOpcode(pOpcode);
|
|
}
|
|
else if ((opcode & 0x7e) == 0x00) // store
|
|
{
|
|
}
|
|
else if ((opcode & 0x7a) == 0x02) // repeat, repeats
|
|
{
|
|
nOpSize += SkipSignedOffset(pOpcode);
|
|
}
|
|
else if ((opcode & 0x78) == 8) // randf, randr
|
|
{
|
|
}
|
|
else if ((opcode & 0x7c) == 0x10) // sexb
|
|
{
|
|
}
|
|
else if ((opcode & 0x7c) == 0x14) // sexw
|
|
{
|
|
}
|
|
else if ((opcode & 0x7c) == 0x18) // postclr
|
|
{
|
|
}
|
|
else if ((opcode & 0x7c) == 0x1c) // postset
|
|
{
|
|
}
|
|
else if ((opcode & 0x78) == 0x20) // preinc
|
|
{
|
|
}
|
|
else if ((opcode & 0x78) == 0x28) // postinc
|
|
{
|
|
}
|
|
else if ((opcode & 0x78) == 0x30) // predec
|
|
{
|
|
}
|
|
else if ((opcode & 0x78) == 0x38) // postdec
|
|
{
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
printf("NOT IMPLEMENTED\n");
|
|
#endif
|
|
}
|
|
|
|
return nOpSize;
|
|
}
|
|
|
|
|
|
int ScanMemoryOpcode(unsigned char* pOpcode)
|
|
{
|
|
int opcode = *pOpcode;
|
|
int memfunc = opcode & 3;
|
|
|
|
int nOpSize = 1;
|
|
|
|
if (opcode < 0x80) // Compact offset
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if ((opcode & 0x0c) >> 2)
|
|
{
|
|
nOpSize += SkipUnsignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
}
|
|
|
|
if (memfunc == 3) // la
|
|
{
|
|
}
|
|
else if (memfunc == 0) // ld
|
|
{
|
|
}
|
|
else if (memfunc == 1) // st
|
|
{
|
|
}
|
|
else // ex
|
|
{
|
|
opcode = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
|
|
nOpSize += ScanExtraOpcode(&pOpcode[nOpSize], opcode);
|
|
}
|
|
|
|
return nOpSize;
|
|
}
|
|
|
|
int ScanRegisterOpcode(unsigned char* pOpcode, int operand)
|
|
{
|
|
int opcode;
|
|
int nOpSize = 0;
|
|
int memfunc = (operand >> 5) & 3;
|
|
|
|
if (memfunc == 1) // store
|
|
{
|
|
}
|
|
else if (memfunc == 0) // load
|
|
{
|
|
}
|
|
else if (memfunc == 2) // execute
|
|
{
|
|
opcode = *pOpcode;
|
|
nOpSize++;
|
|
|
|
nOpSize += ScanExtraOpcode(&pOpcode[nOpSize], opcode);
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
printf("Undefined register operation\n");
|
|
#endif
|
|
}
|
|
|
|
return nOpSize;
|
|
}
|
|
|
|
int ScanLowerOpcode(unsigned char* pOpcode, MethodUsage* pUsage, ObjectEntry* pObject, unsigned char* pMethodStart)
|
|
{
|
|
int opcode = *pOpcode;
|
|
int nOpSize = 1;
|
|
|
|
if (opcode <= 3) // ldfrmr, ldfrm, ldfrmar, ldfrma
|
|
{
|
|
}
|
|
else if (opcode == 0x04) // jmp
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode >= 0x05 && opcode <= 0x07) // call, callobj, callobjx
|
|
{
|
|
int objnum = 0;
|
|
|
|
if (opcode > 0x05)
|
|
{
|
|
objnum = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
if (opcode == 0x07)
|
|
{
|
|
// indexed
|
|
}
|
|
|
|
// skip over invalid calls (happens when we scan strings as opcodes, this can go away when we fix scanning strings properly)
|
|
if (objnum < 0 || objnum > (pObject->nObjectMethodCount + pObject->nObjectSubObjectCount))
|
|
{
|
|
return nOpSize + 1;
|
|
}
|
|
}
|
|
|
|
int pubnum = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
|
|
// skip over invalid calls (happens when we scan strings as opcodes, this can go away when we fix scanning strings properly)
|
|
if (objnum == 0 && (pubnum < 0 || pubnum > pObject->nObjectMethodCount))
|
|
{
|
|
return nOpSize;
|
|
}
|
|
|
|
// need to update usage here
|
|
if (pUsage->pCalls == 0)
|
|
{
|
|
pUsage->nCalls++;
|
|
}
|
|
else
|
|
{
|
|
pUsage->pCalls[pUsage->nCurrCall].opcode = (unsigned char)opcode;
|
|
pUsage->pCalls[pUsage->nCurrCall].pubnum = (unsigned char)pubnum;
|
|
pUsage->pCalls[pUsage->nCurrCall].callOffset = (unsigned int)((&pOpcode[1]) - pMethodStart);
|
|
|
|
if (opcode > 0x05)
|
|
{
|
|
pUsage->pCalls[pUsage->nCurrCall].objnum = (unsigned char)objnum;
|
|
pUsage->pCalls[pUsage->nCurrCall].objoffset = pObject->pIndexTable[objnum-1].offset;
|
|
pUsage->pCalls[pUsage->nCurrCall].objaddress = &pObject->pObject[pUsage->pCalls[pUsage->nCurrCall].objoffset];
|
|
#ifdef RPE_DEBUG
|
|
printf(" callobj %02X:%02X (%p)\n", pUsage->pCalls[pUsage->nCurrCall].objnum, pUsage->pCalls[pUsage->nCurrCall].pubnum, pUsage->pCalls[pUsage->nCurrCall].objaddress);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pUsage->pCalls[pUsage->nCurrCall].objnum = 0;
|
|
pUsage->pCalls[pUsage->nCurrCall].objoffset = 0;
|
|
pUsage->pCalls[pUsage->nCurrCall].objaddress = (pObject->pObject);
|
|
#ifdef RPE_DEBUG
|
|
printf(" call %02X (%p)\n", pUsage->pCalls[pUsage->nCurrCall].pubnum, pUsage->pCalls[pUsage->nCurrCall].objaddress);
|
|
#endif
|
|
}
|
|
pUsage->nCurrCall++;
|
|
}
|
|
}
|
|
else if (opcode == 0x08) // tjz
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode == 0x09) // djnz
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode == 0x0a) // jz
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode == 0x0b) // jnz
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode >= 0x0c && opcode <= 0x15)
|
|
{
|
|
if (opcode == 0x0c) // casedone
|
|
{
|
|
}
|
|
else if (opcode == 0x0d) // casevalue
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode == 0x0e) // caserange
|
|
{
|
|
nOpSize += SkipSignedOffset(&pOpcode[nOpSize]);
|
|
}
|
|
else if (opcode == 0x0f) // lookdone
|
|
{
|
|
}
|
|
else if (opcode == 0x10) // lookupval
|
|
{
|
|
}
|
|
else if (opcode == 0x11) // lookdnval
|
|
{
|
|
}
|
|
else if (opcode == 0x12) // lookuprng
|
|
{
|
|
}
|
|
else if (opcode == 0x13) // lookdnrng
|
|
{
|
|
}
|
|
else if (opcode == 0x14) // pop
|
|
{
|
|
}
|
|
else if (opcode == 0x15) // run
|
|
{
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
printf("%2.2x - NOT IMPLEMENTED\n", opcode);
|
|
#endif
|
|
}
|
|
}
|
|
else if (opcode >= 0x16 && opcode <= 0x23)
|
|
{
|
|
if (opcode == 0x16) // strsize
|
|
{
|
|
}
|
|
else if (opcode == 0x17) // strcomp
|
|
{
|
|
}
|
|
else if (opcode == 0x18) // bytefill
|
|
{
|
|
}
|
|
else if (opcode == 0x19) // wordfill
|
|
{
|
|
}
|
|
else if (opcode == 0x1a) // longfill
|
|
{
|
|
}
|
|
else if (opcode == 0x1b) // waitpeq
|
|
{
|
|
}
|
|
else if (opcode >= 0x1c && opcode <= 0x1e ) // bytemove, wordmove, longmove
|
|
{
|
|
}
|
|
else if (opcode == 0x1f) // waitpne
|
|
{
|
|
}
|
|
else if (opcode == 0x20) // clkset
|
|
{
|
|
}
|
|
else if (opcode == 0x21) // cogstop
|
|
{
|
|
}
|
|
else if (opcode == 0x22) // lockret
|
|
{
|
|
}
|
|
else if (opcode == 0x23) // waitcnt
|
|
{
|
|
}
|
|
}
|
|
else if (opcode >= 0x24 && opcode <= 0x2f)
|
|
{
|
|
if (opcode >= 0x24 && opcode <= 0x26) // ldregx, stregx, exregx
|
|
{
|
|
int operand = ((opcode & 3) << 5);
|
|
nOpSize += ScanRegisterOpcode(&pOpcode[nOpSize], operand);
|
|
}
|
|
else if (opcode == 0x27) // waitvid
|
|
{
|
|
}
|
|
else if (opcode == 0x28 || opcode == 0x2c) // coginitret, coginit
|
|
{
|
|
}
|
|
else if (opcode == 0x29 || opcode == 0x2d) // locknewret, locknew
|
|
{
|
|
}
|
|
else if (opcode == 0x2a || opcode == 0x2b || opcode == 0x2e || opcode == 0x2f) // locksetret, lockclrret, lockset, lockclr
|
|
{
|
|
}
|
|
}
|
|
else if (opcode >= 0x30 && opcode <= 0x33) // abort, abortval, ret, retval
|
|
{
|
|
}
|
|
else if (opcode >= 0x34 && opcode < 0x3c)
|
|
{
|
|
if (opcode == 0x35) // dli0
|
|
{
|
|
}
|
|
else if (opcode == 0x36) // dli1
|
|
{
|
|
}
|
|
else if (opcode == 0x34) // dlim1
|
|
{
|
|
}
|
|
else if (opcode == 0x37) // ldlip
|
|
{
|
|
nOpSize++;
|
|
}
|
|
else // ldbi, ldwi, ldmi, ldli
|
|
{
|
|
while (opcode-- >= 0x38)
|
|
{
|
|
nOpSize++;
|
|
}
|
|
}
|
|
}
|
|
else if (opcode == 0x3d) // ldregbit, stregbit, exregbit
|
|
{
|
|
int operand = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
|
|
nOpSize += ScanRegisterOpcode(&pOpcode[nOpSize], operand);
|
|
}
|
|
else if (opcode == 0x3e) // ldregbits, stregbits, exregbits
|
|
{
|
|
int operand = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
|
|
nOpSize += ScanRegisterOpcode(&pOpcode[nOpSize], operand);
|
|
}
|
|
else if (opcode == 0x3f) // ldreg, streg, exreg
|
|
{
|
|
int operand = pOpcode[nOpSize];
|
|
nOpSize++;
|
|
|
|
nOpSize += ScanRegisterOpcode(&pOpcode[nOpSize], operand);
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
printf("NOT PROCESSED\n");
|
|
#endif
|
|
}
|
|
|
|
return nOpSize;
|
|
}
|
|
|
|
|
|
int ScanOpcode(unsigned char* pOpcode, MethodUsage* pUsage, ObjectEntry* pObject, unsigned char* pMethodStart)
|
|
{
|
|
if (*pOpcode < 0x40)
|
|
{
|
|
return ScanLowerOpcode(pOpcode, pUsage, pObject, pMethodStart);
|
|
}
|
|
else if (*pOpcode < 0xe0)
|
|
{
|
|
return ScanMemoryOpcode(pOpcode);
|
|
}
|
|
else
|
|
{
|
|
return ScanMathOpcode(pOpcode);
|
|
}
|
|
}
|
|
|
|
|
|
void ScanMethod(unsigned char* pMethod, MethodUsage* pUsage, ObjectEntry* pObject)
|
|
{
|
|
#ifdef RPE_DEBUG
|
|
for (int i = 0; i < pUsage->nLength; i++)
|
|
{
|
|
printf("%02x ", pMethod[i]);
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
// scan once to count calls
|
|
int nOffset = 0;
|
|
while (nOffset < pUsage->nLength)
|
|
{
|
|
nOffset += ScanOpcode(&pMethod[nOffset], pUsage, pObject, pMethod);
|
|
}
|
|
if (pUsage->nCalls > 0)
|
|
{
|
|
// if there were calls then allocate space and scan again to fill in call info
|
|
pUsage->pCalls = new CallEntry[pUsage->nCalls];
|
|
nOffset = 0;
|
|
while (nOffset < pUsage->nLength)
|
|
{
|
|
nOffset += ScanOpcode(&pMethod[nOffset], pUsage, pObject, pMethod);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScanObjectMethods(ObjectEntry* pObjectEntry)
|
|
{
|
|
for (int i = 0; i < pObjectEntry->nObjectMethodCount; i++)
|
|
{
|
|
unsigned char* pMethod = pObjectEntry->pObject + pObjectEntry->pIndexTable[i].offset;
|
|
int nLength = 0;
|
|
if (i < pObjectEntry->nObjectMethodCount-1)
|
|
{
|
|
nLength = pObjectEntry->pIndexTable[i+1].offset - pObjectEntry->pIndexTable[i].offset;
|
|
}
|
|
else
|
|
{
|
|
int nNextObjectOffset = *((unsigned short *)(pObjectEntry->pObject));
|
|
nLength = nNextObjectOffset - pObjectEntry->pIndexTable[i].offset;
|
|
}
|
|
pObjectEntry->pMethods[i].nLength = nLength;
|
|
ScanMethod(pMethod, &(pObjectEntry->pMethods[i]), pObjectEntry);
|
|
}
|
|
}
|
|
|
|
void MarkCalls(MethodUsage* pMethod, ObjectEntry* pObject)
|
|
{
|
|
if (pMethod->nCalled == 0)
|
|
{
|
|
pMethod->nCalled = 1;
|
|
pObject->nMethodsCalled++;
|
|
|
|
for (int nCall = 0; nCall < pMethod->nCalls; nCall++)
|
|
{
|
|
CallEntry* pCall = &(pMethod->pCalls[nCall]);
|
|
if ( pCall->opcode == 5 ) // normal call
|
|
{
|
|
MarkCalls(&(pObject->pMethods[pCall->pubnum-1]), pObject);
|
|
}
|
|
else // obj call
|
|
{
|
|
ObjectEntry* pSubObject = GetObject(pCall->objaddress);
|
|
MarkCalls(&(pSubObject->pMethods[pCall->pubnum-1]), pSubObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FindUnusedMethods(CompilerData* pCompilerData)
|
|
{
|
|
for (int i = 0; i < (file_limit * file_limit); i++)
|
|
{
|
|
s_objects[i].pObject = 0;
|
|
s_objects[i].nObjectMethodCount = 0;
|
|
s_objects[i].nObjectSubObjectCount = 0;
|
|
s_objects[i].nObjectIndexCount = 0;
|
|
s_objects[i].nMethodsCalled = 0;
|
|
s_objects[i].nNewObjectIndex = 0;
|
|
s_objects[i].pIndexTable = 0;
|
|
s_objects[i].pMethods = 0;
|
|
}
|
|
s_nNumObjects = 0;
|
|
|
|
int nCompileIndex = 0;
|
|
BuildTables(&(pCompilerData->obj[4]), 0, nCompileIndex);
|
|
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
ScanObjectMethods(&s_objects[i]);
|
|
}
|
|
|
|
ObjectEntry* pObject = &(s_objects[0]);
|
|
MethodUsage* pMethod = &(pObject->pMethods[0]);
|
|
MarkCalls(pMethod, pObject);
|
|
|
|
for (int i = 0; i < s_nNumObjects; i++)
|
|
{
|
|
CheckForCogNewOrInit(&s_objects[i]);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// TERMS OF USE: MIT License //
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
|
|
// software and associated documentation files (the "Software"), to deal in the Software //
|
|
// without restriction, including without limitation the rights to use, copy, modify, //
|
|
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
|
|
// permit persons to whom the Software is furnished to do so, subject to the following //
|
|
// conditions: //
|
|
// //
|
|
// The above copyright notice and this permission notice shall be included in all copies //
|
|
// or substantial portions of the Software. //
|
|
// //
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
|
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
|
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|