/* * symbfile.c * * This file is part of Emu48 * * Copyright (C) 2008 Christoph Gießelink * */ #include "pch.h" #include "Emu48.h" //################ //# //# Saturn Object File Reading //# //################ #define RECORD_BLOCK 256 // block size #define OS_RESOLVED 0x8000 // resolved symbol #define OS_RELOCATABLE 0x4000 // relocatable symbol #define SAT_ID "Saturn3" // saturn block header #define SYMB_ID "Symb" // symbol block header #define HASHENTRIES 199 // size of hash table typedef struct RefData { LPTSTR lpszName; // symbol name DWORD dwAddr; // resolved address struct RefData* pNext; } RefData; static RefData *ppsBase[HASHENTRIES]; // base of symbol references (initialized with NULL) static __inline DWORD GetHash(DWORD dwVal) { return dwVal % HASHENTRIES; // hash function } static DWORD GetBigEndian(LPBYTE pbyData, INT nSize) { DWORD dwVal = 0; while (nSize-- > 0) { dwVal <<= 8; dwVal += *pbyData++; } return dwVal; } // // check if entry table is empty // BOOL RplTableEmpty(VOID) { DWORD i; BOOL bEmpty = TRUE; // check if hash table is empty for (i = 0; bEmpty && i < ARRAYSIZEOF(ppsBase); ++i) { bEmpty = (ppsBase[i] == NULL); // check if empty } return bEmpty; } // // load entry table // BOOL RplLoadTable(LPCTSTR lpszFilename) { BYTE byPage[RECORD_BLOCK]; // record page size HANDLE hFile; DWORD dwFileLength,dwCodeLength,dwNoSymbols,dwNoReferences; DWORD dwBytesRead,dwSymb,dwPageIndex,dwResolvedSymb; BOOL bSymbol; hFile = CreateFile(lpszFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hFile == INVALID_HANDLE_VALUE) { RplDeleteTable(); // delete current table return FALSE; } // read first page ReadFile(hFile,byPage,sizeof(byPage),&dwBytesRead,NULL); if ( dwBytesRead != sizeof(byPage) || memcmp(byPage,SAT_ID,7) != 0) { RplDeleteTable(); // delete current table CloseHandle(hFile); return FALSE; } // file length in bytes dwFileLength = GetBigEndian(byPage+7,sizeof(WORD)) * sizeof(byPage); // code area in nibbles dwCodeLength = GetBigEndian(byPage+9,sizeof(DWORD)); // no. of symbols & references dwNoSymbols = GetBigEndian(byPage+13,sizeof(WORD)); // no. of references dwNoReferences = GetBigEndian(byPage+15,sizeof(WORD)); // convert code area length into no. of pages dwPageIndex = (dwCodeLength + (2 * sizeof(byPage) - 1)) / (2 * sizeof(byPage)); // jump to begin of symbols by skipping no. of code pages if (SetFilePointer(hFile,dwPageIndex*sizeof(byPage),NULL,FILE_CURRENT) == INVALID_SET_FILE_POINTER) { RplDeleteTable(); // delete current table CloseHandle(hFile); return FALSE; } dwResolvedSymb = 0; // no resolved symbols added bSymbol = TRUE; // next set is a symbol // read all symbol pages for (dwPageIndex = 256, dwSymb = 0; dwSymb < dwNoSymbols; dwPageIndex += 42) { if (dwPageIndex >= 256) // read complete page { // read new symbol page ReadFile(hFile,byPage,sizeof(byPage),&dwBytesRead,NULL); if ( dwBytesRead != sizeof(byPage) || memcmp(byPage,SYMB_ID,4) != 0) { RplDeleteTable(); // delete current table CloseHandle(hFile); return FALSE; } dwPageIndex = 4; // begin of new symbol } if (bSymbol) // this is the 42 byte symbol set { WORD wSymbolType = (WORD) GetBigEndian(byPage+dwPageIndex+36,sizeof(WORD)); // check if it's a resolved or relocatable symbol bSymbol = (wSymbolType & OS_RESOLVED) != 0; if (bSymbol) ++dwResolvedSymb; // added resolved symbol if (wSymbolType == OS_RESOLVED) // resolved symbol type { TCHAR szSymbolName[36+1],*pcPtr; RefData *pData; DWORD dwHash; #if defined _UNICODE { MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,byPage+dwPageIndex,36, szSymbolName,ARRAYSIZEOF(szSymbolName)); szSymbolName[36] = 0; // set EOS } #else { lstrcpyn(szSymbolName,byPage+dwPageIndex,ARRAYSIZEOF(szSymbolName)); } #endif // cut symbol name at first space character if ((pcPtr = _tcschr(szSymbolName,_T(' '))) != NULL) *pcPtr = 0; // set EOS // allocate symbol memory VERIFY(pData = HeapAlloc(hHeap,0,sizeof(RefData))); pData->lpszName = DuplicateString(szSymbolName); pData->dwAddr = GetBigEndian(byPage+dwPageIndex+38,sizeof(DWORD)); // add to hash table dwHash = GetHash(pData->dwAddr); pData->pNext = ppsBase[dwHash]; ppsBase[dwHash] = pData; } ++dwSymb; // got symbol } else // 42 byte fill reference { bSymbol = TRUE; // nothing to do, next is a symbol set } } _ASSERT(dwResolvedSymb == (dwNoSymbols - dwNoReferences)); CloseHandle(hFile); return TRUE; } // // delete entry table // VOID RplDeleteTable(VOID) { RefData *pData; DWORD i; // clear hash entries for (i = 0; i < ARRAYSIZEOF(ppsBase); ++i) { while (ppsBase[i] != NULL) // walk through all datasets { pData = ppsBase[i]->pNext; HeapFree(hHeap,0,ppsBase[i]->lpszName); HeapFree(hHeap,0,ppsBase[i]); ppsBase[i] = pData; } } return; } // // return name for given entry address // LPCTSTR RplGetName(DWORD dwAddr) { RefData *pData = ppsBase[GetHash(dwAddr)]; // walk through all datasets of hash entry for (; pData != NULL; pData = pData->pNext) { if (pData->dwAddr == dwAddr) // found address return pData->lpszName; // return symbol name } return NULL; } // // return entry address for given name // BOOL RplGetAddr(LPCTSTR lpszName, DWORD *pdwAddr) { RefData *pData; DWORD i; // check for every dataset in hash table for (i = 0; i < ARRAYSIZEOF(ppsBase); ++i) { // walk through all datasets of hash entry for (pData = ppsBase[i]; pData != NULL; pData = pData->pNext) { // found symbol name if (lstrcmp(lpszName,pData->lpszName) == 0) { *pdwAddr = pData->dwAddr; // return address return FALSE; // found } } } return TRUE; // not found }