From b3d9b40563e713a76c38bcd7a99e66472c92e77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Thu, 9 Jan 2020 16:01:16 -0800 Subject: [PATCH] push/pull symbols and strings with directiv --- test/ca65directive.s | 7 ++ x65.cpp | 276 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 280 insertions(+), 3 deletions(-) diff --git a/test/ca65directive.s b/test/ca65directive.s index 6d3974e..a89f7a2 100644 --- a/test/ca65directive.s +++ b/test/ca65directive.s @@ -2,6 +2,13 @@ .org $2000 +test_stack = 0 +PUSH test_stack +test_stack = 10 +eval test_stack +PULL test_stack +eval test_stack + zp_len_lo = $a7 zp_len_hi = $a8 diff --git a/x65.cpp b/x65.cpp index f4507ce..c58ef00 100644 --- a/x65.cpp +++ b/x65.cpp @@ -39,6 +39,7 @@ #include #include #include +#include // Command line arguments static const strref cmdarg_listing("lst"); // -lst / -lst=(file.lst) : generate disassembly text from result(file or stdout) @@ -138,6 +139,7 @@ enum StatusCode { ERROR_DS_MUST_EVALUATE_IMMEDIATELY, ERROR_NOT_AN_X65_OBJECT_FILE, ERROR_COULD_NOT_INCLUDE_FILE, + ERROR_PULL_WITHOUT_PUSH, ERROR_USER, ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution @@ -210,6 +212,7 @@ const char *aStatusStrings[STATUSCODE_COUNT] = { "DS directive failed to evaluate immediately", // ERROR_DS_MUST_EVALUATE_IMMEDIATELY, "File is not a valid x65 object file", // ERROR_NOT_AN_X65_OBJECT_FILE, "Failed to read include file", // ERROR_COULD_NOT_INCLUDE_FILE, + "Using symbol PULL without first using a PUSH", // ERROR_PULL_WITHOUT_PUSH "User invoked error", // ERROR_USER, "Errors after this point will stop execution", // ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution @@ -295,6 +298,8 @@ enum AssemblerDirective { AD_DUMMY_END, // DEND: End a dummy section AD_SCOPE, // SCOPE: Begin ca65 style scope AD_ENDSCOPE, // ENDSCOPR: End ca65 style scope + AD_PUSH, // PUSH: Push the value of a variable symbol on a stack + AD_PULL, // PULL: Pull the value of a variable symbol from its stack, must be pushed first AD_DS, // DS: Define section, zero out # bytes or rewind the address if negative AD_USR, // USR: MERLIN user defined pseudo op, runs some code at a hard coded address on apple II, on PC does nothing. AD_SAV, // SAV: MERLIN version of export but contains full filename, not an appendable name @@ -1008,9 +1013,11 @@ DirectiveName aDirectiveNames[] { { "I8", AD_XY8 }, // I8: Set 8 bit index register mode { "DUMMY", AD_DUMMY }, { "DUMMY_END", AD_DUMMY_END }, + { "DS", AD_DS }, // Define space { "SCOPE", AD_SCOPE }, // SCOPE: Begin ca65 style scope { "ENDSCOPE", AD_ENDSCOPE },// ENDSCOPR: End ca65 style scope - { "DS", AD_DS }, // Define space + { "PUSH", AD_PUSH }, + { "PULL", AD_PULL }, { "ABORT", AD_ABORT }, { "ERR", AD_ABORT }, // DASM version of ABORT }; @@ -1159,6 +1166,176 @@ public: } }; + + +template< class KeyType, class ValueType, class CountType = size_t > struct HashTable { + CountType size, maxSteps, used; + KeyType* keys; + ValueType* values; + + static CountType HashFunction(KeyType v) { return CountType(((v + (v >> 27) + (v << 29)) + 14695981039346656037) * 1099511628211); } + static CountType HashIndex(KeyType hash, CountType tableSize) { return hash & (tableSize - 1); } + static CountType GetNextIndex(KeyType hash, CountType tableSize) { return (hash + 1) & (tableSize - 1); } + static CountType KeyToIndex(KeyType key, CountType tableSize) { return HashIndex(HashFunction(key), tableSize); } + static CountType FindKeyIndex(KeyType hash, CountType hashTableSize, KeyType* hashKeys, CountType maxKeySteps) { + CountType index = KeyToIndex(hash, hashTableSize); + while (hashKeys) { + KeyType key = hashKeys[index]; + if (!key || key == hash) { return index; } + index = GetNextIndex(index, hashTableSize); + if (!maxKeySteps--) { break; } + } + return index; + } + + CountType KeyToIndex(KeyType key) { return KeyToIndex(key, size); } + + CountType InsertKey(KeyType key, CountType index) { + const KeyType* hashKeys = keys; + CountType currSize = size; + CountType insertSteps = 0; + while (KeyType k = hashKeys[index]) { + if (k == key) { return index; } // key already exists + CountType kfirst = KeyToIndex(k, currSize); + CountType ksteps = kfirst > index ? (currSize + index - kfirst) : (index - kfirst); + if (insertSteps > ksteps) { return index; } + index = GetNextIndex(index, size); + ++insertSteps; + } + return index; + } + + CountType FindKeyIndex(KeyType hash) const { return FindKeyIndex(hash, size, keys, maxSteps); } + + CountType Steps(KeyType hash) { + CountType slot = KeyToIndex(hash, size); + CountType numSteps = 0; + while (keys[slot] && keys[slot] != hash) { + ++numSteps; + slot = GetNextIndex(slot, size); + } + return numSteps; + } + + void UpdateSteps(CountType first, CountType slot) { + CountType steps = slot > first ? (slot - first) : (size + slot - first); + if (steps > maxSteps) { maxSteps = steps; } + } + + ValueType* InsertFitted(KeyType key) { + assert(key); // key may not be 0 + CountType first = KeyToIndex(key); + CountType slot = InsertKey(key, first); + UpdateSteps(first, slot); + if (keys[slot]) { + if (keys[slot] == key) { return &values[slot]; } else { + KeyType prvKey = keys[slot]; + ValueType prev_value = values[slot]; + keys[slot] = key; + for (;; ) { + CountType prev_first = KeyToIndex(prvKey); + CountType slotRH = InsertKey(prvKey, prev_first); + UpdateSteps(prev_first, slotRH); + if (keys[slotRH] && keys[slotRH] != prvKey) { + KeyType tmpKey = keys[slotRH]; + keys[slotRH] = prvKey; + prvKey = tmpKey; + ValueType temp_value = values[slotRH]; + values[slotRH] = prev_value; + prev_value = temp_value; + } else { + keys[slotRH] = prvKey; + values[slotRH] = prev_value; + ++used; + return &values[slot]; + } + } + } + } + keys[slot] = key; + ++used; + return &values[slot]; + } + + HashTable() { Reset(); } + + void Reset() { + used = 0; + size = 0; + maxSteps = 0; + keys = nullptr; + values = nullptr; + } + + ~HashTable() { Clear(); } + + void Clear() { + if (values) { + for (CountType i = 0, n = size; i < n; ++i) { + values[i].~ValueType(); + } + free(values); + } + if (keys) { free(keys); } + Reset(); + } + + CountType GetUsed() const { return used; } + bool TableMax() const { return used && (used << 4) >= (size * 13); } + + void Grow() { + KeyType *prevKeys = keys; + ValueType *prevValues = values; + CountType prevSize = size, newSize = prevSize ? (prevSize << 1) : 64; + size = newSize; + keys = (KeyType*)calloc(1, newSize * sizeof(KeyType)); + values = (ValueType*)calloc(1, newSize * sizeof(ValueType)); + maxSteps = 0; + for (CountType i = 0; i < newSize; ++i) { new (values + i) ValueType; } + if (used) { + used = 0; + for (CountType i = 0; i < prevSize; i++) { + if (KeyType key = prevKeys[i]) { *InsertFitted(key) = prevValues[i]; } + } + } + if (prevKeys) { free(prevKeys); } + if (prevValues) { + for (CountType i = 0; i != prevSize; ++i) { prevValues[i].~ValueType(); } + free(prevValues); + } + } + + ValueType* InsertKey(KeyType key) + { + if (!size || TableMax()) { Grow(); } + return InsertFitted(key); + } + + ValueType* InsertKeyValue(KeyType key, const ValueType& value) + { + ValueType* value_ptr = InsertKeyValue(key); + *value_ptr = value; + return value_ptr; + } + + bool KeyExists(KeyType key) + { + return size && key && keys[FindKeyIndex(key)] == key; + } + + ValueType* GetValue(KeyType key) + { + if (size && key) { + CountType slot = FindKeyIndex(key); + if (keys[slot] == key) { + return &values[slot]; + } + } + return nullptr; + } +}; + + // relocs are cheaper than full expressions and work with // local labels for relative sections which would otherwise // be out of scope at link time. @@ -1494,6 +1671,17 @@ public: bool empty() const { return stack.size() == 0; } }; +// Support for the PULL and PUSH directives +typedef union { int value; char* string; } ValueOrString; +typedef std::vector < ValueOrString > SymbolStack; +class SymbolStackTable : public HashTable< uint64_t, SymbolStack* > { +public: + void PushSymbol(Label* symbol); + StatusCode PullSymbol(Label* symbol); + void PushSymbol(StringSymbol* string); + StatusCode PullSymbol(StringSymbol* string); +}; + // The state of the assembler class Asm { public: @@ -1514,6 +1702,8 @@ public: std::vector externals; // external labels organized by object file MapSymbolArray map; + SymbolStackTable symbolStacks; // enable push/pull of symbols + // CPU target struct mnem *opcode_table; int opcode_count; @@ -1736,6 +1926,65 @@ public: Cleanup(); localLabels.reserve(256); loadedData.reserve(16); lateEval.reserve(64); } }; + +void SymbolStackTable::PushSymbol(Label* symbol) +{ + uint64_t key = symbol->label_name.fnv1a_64(symbol->pool_name.fnv1a_64()); + SymbolStack** ppStack = InsertKey(key); // ppStack will exist but contains a pointer that may not exist + if (!*ppStack) { *ppStack = new SymbolStack; } + ValueOrString val; + val.value = symbol->value; + (*ppStack)->push_back(val); +} + +StatusCode SymbolStackTable::PullSymbol(Label* symbol) +{ + uint64_t key = symbol->label_name.fnv1a_64(symbol->pool_name.fnv1a_64()); + SymbolStack** ppStack = GetValue(key); + if (!ppStack || !(*ppStack)->size()) { return ERROR_PULL_WITHOUT_PUSH; } + symbol->value = (**ppStack)[(*ppStack)->size() - 1].value; + (*ppStack)->pop_back(); + return STATUS_OK; +} + +void SymbolStackTable::PushSymbol(StringSymbol* string) +{ + uint64_t key = string->string_name.fnv1a_64(); + SymbolStack** ppStack = InsertKey(key); // ppStack will exist but contains a pointer that may not exist + if (!*ppStack) { *ppStack = new SymbolStack; } + ValueOrString val; + val.string = nullptr; + if (string->string_value) { + val.string = (char*)malloc(string->string_value.get_len() + 1); + memcpy(val.string, string->string_value.get(), string->string_value.get_len()); + val.string[string->string_value.get_len()] = 0; + } + (*ppStack)->push_back(val); +} + +StatusCode SymbolStackTable::PullSymbol(StringSymbol* string) +{ + uint64_t key = string->string_name.fnv1a_64(); + SymbolStack** ppStack = GetValue(key); + if (!ppStack || !(*ppStack)->size()) { return ERROR_PULL_WITHOUT_PUSH; } + char* str = (**ppStack)[(*ppStack)->size() - 1].string; + if (!str && string->string_value) { + free(string->string_value.charstr()); + string->string_value.invalidate(); + } else { + if (string->string_value.empty() || string->string_value.cap() < (strlen(str) + 1)) { + if (string->string_value.charstr()) { free(string->string_value.charstr()); } + string->string_value.set_overlay((char*)malloc(strlen(str) + 1), strlen(str) + 1); + } + string->string_value.copy(str); + free(str); + } + (*ppStack)->pop_back(); + return STATUS_OK; +} + + + // Clean up work allocations void Asm::Cleanup() { for (std::vector::iterator i = loadedData.begin(); i != loadedData.end(); ++i) { @@ -5382,6 +5631,9 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc } break; + case AD_DS: + return Directive_DS(line); + case AD_SCOPE: directive_scope_depth++; return EnterScope(); @@ -5390,8 +5642,26 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc directive_scope_depth--; return ExitScope(); - case AD_DS: - return Directive_DS(line); + case AD_PUSH: + line.trim_whitespace(); + if (Label *label = GetLabel(line)) { + symbolStacks.PushSymbol(label); + return STATUS_OK; + } else if( StringSymbol* string = GetString(line)) { + symbolStacks.PushSymbol(string); + return STATUS_OK; + } + return ERROR_UNABLE_TO_PROCESS; + + case AD_PULL: + line.trim_whitespace(); + if (Label *label = GetLabel(line)) { + return symbolStacks.PullSymbol(label); + } else if (StringSymbol* string = GetString(line)) { + return symbolStacks.PullSymbol(string); + } + return ERROR_UNABLE_TO_PROCESS; + } return error; }