/// <summary> /// Initializes a new instance of the Word class whose Name /// Field Address is located at "nfa" in the given ROM. /// </summary> /// <param name="rom">MFORTH ROM.</param> /// <param name="nfa">Name Field Address of the word.</param> public Word(ROM rom, int nfa) { // Store the NFA. this.NFA = nfa; // Get the name of the word. this.Name = GetWordName(rom, nfa); // Get the address of the next (previous) word in the // dictionary. this.NextWordAddr = rom.GetInt16(nfa + NFATOLFASZ); }
/// <summary> /// phashgen entry point. /// </summary> /// <param name="args">ROM path, symbol table path, output /// path.</param> public static void Main(string[] args) { // Load the ROM. var rom = new ROM(args[0]); // Load the symbol table. IDictionary<int, string> symbolTable = LoadSymbolTable(args[1]); // Open the output file. StreamWriter outFile = new StreamWriter(args[2], false, Encoding.ASCII); // Generate the hash table. Console.Write("Generating PHASH tables: "); PearsonHashFunction phf1, phf2; var hashTable = GenerateHashTable(rom, out phf1, out phf2); Console.WriteLine(" Done!"); Console.WriteLine( "Total words: {0}; at first hash location: {1}; at second hash location: {2}", rom.NumWords, hashTable.NumValuesStoredAtFirstHash, hashTable.NumValuesStoredAtSecondHash); // Write out the mask value and auxilliary tables used to // construct the hash table. outFile.WriteLine( "PHASHMASK: .EQU 0{0:X2}h", HashMask >> 8); outFile.WriteLine( "PHASHAUX1: .ORG 0{0:X4}h", ROM.Size - (HashTableSize << 1) - (2 * PearsonHashFunction.AuxilliaryTableSize)); WriteByteData(outFile, phf1.AuxilliaryTable); outFile.WriteLine( "PHASHAUX2: .ORG 0{0:X4}h", ROM.Size - (HashTableSize << 1) - PearsonHashFunction.AuxilliaryTableSize); WriteByteData(outFile, phf2.AuxilliaryTable); // Write out the hash table. outFile.WriteLine( "PHASHTAB: .ORG 0{0:X4}h", ROM.Size - (HashTableSize << 1)); WriteHashTable(outFile, hashTable, symbolTable); // Close the output file; we're done! outFile.Close(); }
/// <summary> /// Generate the MFORTH hash table. /// </summary> /// <param name="rom">MFORTH ROM.</param> /// <param name="phf1">Upon return, will contain the first /// PearsonHashFunction used by the hash table.</param> /// <param name="phf2">Upon return, will contain the second /// PearsonHashFunction used by the hash table.</param> /// <returns>The MFORTH hash table.</returns> private static CuckooHashTable<Word> GenerateHashTable(ROM rom, out PearsonHashFunction phf1, out PearsonHashFunction phf2) { // Initialize our random number generator. var random = new Random(HashTableRandomSeed); // Initialize our hash functions and the hash table. var hashFunc1 = phf1 = new PearsonHashFunction(random); var hashFunc2 = phf2 = new PearsonHashFunction(random); var hashTable = new CuckooHashTable<Word>( (w) => HashName(w.Name, hashFunc1, hashFunc2, HashMask), (w) => HashName(w.Name, hashFunc2, hashFunc1, HashMask)); // Generate the hash table. #if FINDING_OPTIMAL_HASH_SEED int minValuesAtSecondLocation = int.MaxValue; #endif for (;;) { #if !FINDING_OPTIMAL_HASH_SEED // Display a progress dot. Console.Write("."); #endif // Add all of the words to our hash table; assume that // we will be successful. bool haveCompleteTable = true; foreach (var word in rom.Words) { if (!hashTable.TryAddValue(word)) { haveCompleteTable = false; break; } } #if !FINDING_OPTIMAL_HASH_SEED // We're done if all of the words were added to the // table. if (haveCompleteTable) { break; } // We failed to generate a complete hash table; shuffle // the hash functions, clear the table, and try again. phf1.ShuffleAuxilliaryTable(random); phf2.ShuffleAuxilliaryTable(random); hashTable.Clear(); #else // Print out the results if this seed produced better // results than the previous best seed. if (haveCompleteTable && hashTable.NumValuesStoredAtSecondHash < minValuesAtSecondLocation) { Console.WriteLine( "Seed: {0}; 1st: {1}; 2nd: {2}", HashTableRandomSeed, hashTable.NumValuesStoredAtFirstHash, hashTable.NumValuesStoredAtSecondHash); minValuesAtSecondLocation = hashTable.NumValuesStoredAtSecondHash; } // Generate a new seed and re-create all of the hash // functions and tables. random = new Random(++HashTableRandomSeed); hashFunc1 = phf1 = new PearsonHashFunction(random); hashFunc2 = phf2 = new PearsonHashFunction(random); hashTable = new CuckooHashTable<Word>( (w) => HashName(w.Name, hashFunc1, hashFunc2, HashMask), (w) => HashName(w.Name, hashFunc2, hashFunc1, HashMask)); #endif } // Ensure that all of the words in the ROM were added to the // hash table. if (rom.NumWords != hashTable.Count) { throw new InvalidDataException( string.Format( "Hash table only has {0} words; expected {1} words.", hashTable.Count, rom.NumWords)); } // Return the hash table. return hashTable; }
/// <summary> /// Gets the name of the word whose NFA is at the given address. /// </summary> /// <param name="rom">MFORTH ROM.</param> /// <param name="nfa">Name Field Address of the start of the /// name.</param> /// <returns>The name of the word.</returns> private static string GetWordName(ROM rom, int nfa) { // Get the length of the word's name from the byte at the // NFA. int wordLen = rom.GetInt8(nfa) & 0x3f; // Read the bytes in the word's name. Ignore the length for // now and just use the end-of-name flag to determine when // to stop reading bytes. var word = new StringBuilder(); for (int romOffset = nfa - 1;; romOffset--) { byte c = rom.GetInt8(romOffset); word.Append(Encoding.ASCII.GetString(new byte[] { (byte)(c & 0x7F) })); if ((c & 0x80) != 0) { break; } } // Ensure that the decoded name is the correct length. if (word.Length != wordLen) { throw new InvalidDataException( string.Format( "Word '{0}' has NFA with incorrect length {1}.", word, wordLen)); } // Return the name. return word.ToString(); }