diff options
Diffstat (limited to 'src/emu/debug/express.cpp')
-rw-r--r-- | src/emu/debug/express.cpp | 1165 |
1 files changed, 830 insertions, 335 deletions
diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp index bcd9269db30..9ab75b7b655 100644 --- a/src/emu/debug/express.cpp +++ b/src/emu/debug/express.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - express.c + express.cpp Generic expressions engine. @@ -31,7 +31,9 @@ #include "emu.h" #include "express.h" -#include <ctype.h> + +#include "corestr.h" +#include <cctype> @@ -39,19 +41,17 @@ DEBUGGING ***************************************************************************/ -#define DEBUG_TOKENS 0 +#define LOG_OUTPUT_FUNC osd_printf_info +#define VERBOSE 0 +#include "logmacro.h" +namespace { /*************************************************************************** CONSTANTS ***************************************************************************/ -#ifndef DEFAULT_BASE -#define DEFAULT_BASE 16 // hex unless otherwise specified -#endif - - // token.value values if token.is_operator() enum { @@ -104,7 +104,7 @@ enum //************************************************************************** -// TYPE DEFINITIONS +// REGISTER SYMBOL ENTRY //************************************************************************** // a symbol entry representing a register, with read/write callbacks @@ -117,8 +117,8 @@ public: integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format); // symbol access - virtual bool is_lval() const override; - virtual u64 value() const override; + virtual bool is_lval() const override { return m_setter != nullptr; } + virtual u64 value() const override { return m_getter(); } virtual void set_value(u64 newvalue) override; private: @@ -129,6 +129,61 @@ private: }; +//------------------------------------------------- +// integer_symbol_entry - constructor +//------------------------------------------------- + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr) + : symbol_entry(table, SMT_INTEGER, name, ""), + m_getter(ptr + ? symbol_table::getter_func([ptr] () { return *ptr; }) + : symbol_table::getter_func([this] () { return m_value; })), + m_setter((rw == symbol_table::READ_ONLY) + ? symbol_table::setter_func(nullptr) + : ptr + ? symbol_table::setter_func([ptr] (u64 value) { *ptr = value; }) + : symbol_table::setter_func([this] (u64 value) { m_value = value; })), + m_value(0) +{ +} + + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval) + : symbol_entry(table, SMT_INTEGER, name, ""), + m_getter([this] () { return m_value; }), + m_setter(nullptr), + m_value(constval) +{ +} + + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format) + : symbol_entry(table, SMT_INTEGER, name, format), + m_getter(std::move(getter)), + m_setter(std::move(setter)), + m_value(0) +{ +} + + +//------------------------------------------------- +// set_value - set the value of this symbol +//------------------------------------------------- + +void integer_symbol_entry::set_value(u64 newvalue) +{ + if (m_setter != nullptr) + m_setter(newvalue); + else + throw emu_fatalerror("Symbol '%s' is read-only", m_name); +} + + + +//************************************************************************** +// FUNCTION SYMBOL ENTRY +//************************************************************************** + // a symbol entry representing a function class function_symbol_entry : public symbol_entry { @@ -136,8 +191,12 @@ public: // construction/destruction function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute); + // getters + u16 minparams() const { return m_minparams; } + u16 maxparams() const { return m_maxparams; } + // symbol access - virtual bool is_lval() const override; + virtual bool is_lval() const override { return false; } virtual u64 value() const override; virtual void set_value(u64 newvalue) override; @@ -152,6 +211,82 @@ private: }; +//------------------------------------------------- +// function_symbol_entry - constructor +//------------------------------------------------- + +function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute) + : symbol_entry(table, SMT_FUNCTION, name, ""), + m_minparams(minparams), + m_maxparams(maxparams), + m_execute(std::move(execute)) +{ +} + + +//------------------------------------------------- +// value - return the value of this symbol +//------------------------------------------------- + +u64 function_symbol_entry::value() const +{ + throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name); +} + + +//------------------------------------------------- +// set_value - set the value of this symbol +//------------------------------------------------- + +void function_symbol_entry::set_value(u64 newvalue) +{ + throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name); +} + + +//------------------------------------------------- +// execute - execute the function +//------------------------------------------------- + +u64 function_symbol_entry::execute(int numparams, const u64 *paramlist) +{ + if (numparams < m_minparams) + throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name, m_minparams); + if (numparams > m_maxparams) + throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name, m_maxparams); + return m_execute(numparams, paramlist); +} + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +inline std::pair<device_t &, char const *> get_device_search(running_machine &machine, device_memory_interface *memintf, char const *tag) +{ + if (tag) + { + if (('.' == tag[0]) && (!tag[1] || (':' == tag[1]) || ('^' == tag[1]))) + return std::pair<device_t &, char const *>(memintf ? memintf->device() : machine.root_device(), tag + ((':' == tag[1]) ? 2 : 1)); + else if (('^' == tag[0]) && memintf) + return std::pair<device_t &, char const *>(memintf->device(), tag); + else + return std::pair<device_t &, char const *>(machine.root_device(), tag); + } + else if (memintf) + { + return std::pair<device_t &, char const *>(memintf->device(), ""); + } + else + { + return std::pair<device_t &, char const *>(machine.root_device(), ""); + } +} + +} // anonymous namespace + + //************************************************************************** // EXPRESSION ERROR @@ -162,7 +297,7 @@ private: // given expression error //------------------------------------------------- -const char *expression_error::code_string() const +std::string expression_error::code_string() const { switch (m_code) { @@ -178,6 +313,8 @@ const char *expression_error::code_string() const case DIVIDE_BY_ZERO: return "divide by zero"; case OUT_OF_MEMORY: return "out of memory"; case INVALID_PARAM_COUNT: return "invalid number of parameters"; + case TOO_FEW_PARAMS: return util::string_format("too few parameters (at least %d required)", m_num); + case TOO_MANY_PARAMS: return util::string_format("too many parameters (no more than %d accepted)", m_num); case UNBALANCED_QUOTES: return "unbalanced quotes"; case TOO_MANY_STRINGS: return "too many strings"; case INVALID_MEMORY_SIZE: return "invalid memory size (b/w/d/q expected)"; @@ -200,8 +337,7 @@ const char *expression_error::code_string() const //------------------------------------------------- symbol_entry::symbol_entry(symbol_table &table, symbol_type type, const char *name, const std::string &format) - : m_next(nullptr), - m_table(table), + : m_table(table), m_type(type), m_name(name), m_format(format) @@ -220,312 +356,652 @@ symbol_entry::~symbol_entry() //************************************************************************** -// REGISTER SYMBOL ENTRY +// SYMBOL TABLE //************************************************************************** //------------------------------------------------- -// integer_symbol_entry - constructor +// symbol_table - constructor //------------------------------------------------- -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr) - : symbol_entry(table, SMT_INTEGER, name, ""), - m_getter(ptr - ? symbol_table::getter_func([ptr] (symbol_table &table) { return *ptr; }) - : symbol_table::getter_func([this] (symbol_table &table) { return m_value; })), - m_setter((rw == symbol_table::READ_ONLY) - ? symbol_table::setter_func(nullptr) - : ptr - ? symbol_table::setter_func([ptr] (symbol_table &table, u64 value) { *ptr = value; }) - : symbol_table::setter_func([this] (symbol_table &table, u64 value) { m_value = value; })), - m_value(0) -{ -} - - -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval) - : symbol_entry(table, SMT_INTEGER, name, ""), - m_getter([this] (symbol_table &table) { return m_value; }), - m_setter(nullptr), - m_value(constval) -{ -} - - -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format) - : symbol_entry(table, SMT_INTEGER, name, format), - m_getter(getter), - m_setter(setter), - m_value(0) +symbol_table::symbol_table(running_machine &machine, symbol_table *parent, device_t *device) + : m_machine(machine) + , m_parent(parent) + , m_memintf(dynamic_cast<device_memory_interface *>(device)) + , m_memory_modified(nullptr) { } //------------------------------------------------- -// is_lval - is this symbol allowable as an lval? +// set_memory_modified_func - install notifier +// for when memory is modified in debugger //------------------------------------------------- -bool integer_symbol_entry::is_lval() const +void symbol_table::set_memory_modified_func(memory_modified_func modified) { - return (m_setter != nullptr); + m_memory_modified = std::move(modified); } //------------------------------------------------- -// value - return the value of this symbol +// add - add a new u64 pointer symbol //------------------------------------------------- -u64 integer_symbol_entry::value() const +symbol_entry &symbol_table::add(const char *name, read_write rw, u64 *ptr) { - return m_getter(m_table); + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)).first->second; } //------------------------------------------------- -// set_value - set the value of this symbol +// add - add a new value symbol //------------------------------------------------- -void integer_symbol_entry::set_value(u64 newvalue) +symbol_entry &symbol_table::add(const char *name, u64 value) { - if (m_setter != nullptr) - m_setter(m_table, newvalue); - else - throw emu_fatalerror("Symbol '%s' is read-only", m_name.c_str()); + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)).first->second; } - -//************************************************************************** -// FUNCTION SYMBOL ENTRY -//************************************************************************** - //------------------------------------------------- -// function_symbol_entry - constructor +// add - add a new register symbol //------------------------------------------------- -function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute) - : symbol_entry(table, SMT_FUNCTION, name, ""), - m_minparams(minparams), - m_maxparams(maxparams), - m_execute(execute) +symbol_entry &symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) { + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)).first->second; } //------------------------------------------------- -// is_lval - is this symbol allowable as an lval? +// add - add a new function symbol //------------------------------------------------- -bool function_symbol_entry::is_lval() const +symbol_entry &symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) { - return false; + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)).first->second; } //------------------------------------------------- -// value - return the value of this symbol +// find_deep - do a deep search for a symbol, +// looking in the parent if needed //------------------------------------------------- -u64 function_symbol_entry::value() const +symbol_entry *symbol_table::find_deep(const char *symbol) { - throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name.c_str()); + // walk up the table hierarchy to find the owner + for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + { + symbol_entry *entry = symtable->find(symbol); + if (entry != nullptr) + return entry; + } + return nullptr; } //------------------------------------------------- -// set_value - set the value of this symbol +// value - return the value of a symbol //------------------------------------------------- -void function_symbol_entry::set_value(u64 newvalue) +u64 symbol_table::value(const char *symbol) { - throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name.c_str()); + symbol_entry *entry = find_deep(symbol); + return (entry != nullptr) ? entry->value() : 0; } //------------------------------------------------- -// execute - execute the function +// set_value - set the value of a symbol //------------------------------------------------- -u64 function_symbol_entry::execute(int numparams, const u64 *paramlist) +void symbol_table::set_value(const char *symbol, u64 value) { - if (numparams < m_minparams) - throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name.c_str(), m_minparams); - if (numparams > m_maxparams) - throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name.c_str(), m_maxparams); - return m_execute(m_table, numparams, paramlist); + symbol_entry *entry = find_deep(symbol); + if (entry != nullptr) + entry->set_value(value); } //************************************************************************** -// SYMBOL TABLE +// EXPRESSION MEMORY HANDLERS //************************************************************************** //------------------------------------------------- -// symbol_table - constructor +// read_memory - return 1,2,4 or 8 bytes +// from the specified memory space //------------------------------------------------- -symbol_table::symbol_table(void *globalref, symbol_table *parent) - : m_parent(parent), - m_globalref(globalref), - m_memory_param(nullptr), - m_memory_valid(nullptr), - m_memory_read(nullptr), - m_memory_write(nullptr) +u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bool apply_translation) { -} + u64 result = ~u64(0) >> (64 - 8*size); + address_space *tspace = &space; -//------------------------------------------------- -// add - add a new u64 pointer symbol -//------------------------------------------------- + if (apply_translation) + { + // mask against the logical byte mask + address &= space.logaddrmask(); -void symbol_table::configure_memory(void *param, valid_func valid, read_func read, write_func write) -{ - m_memory_param = param; - m_memory_valid = valid; - m_memory_read = read; - m_memory_write = write; + // translate if necessary; if not mapped, return 0xffffffffffffffff + if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace)) + return result; + } + + // otherwise, call the reading function for the translated address + switch (size) + { + case 1: result = tspace->read_byte(address); break; + case 2: result = tspace->read_word_unaligned(address); break; + case 4: result = tspace->read_dword_unaligned(address); break; + case 8: result = tspace->read_qword_unaligned(address); break; + } + return result; } //------------------------------------------------- -// add - add a new u64 pointer symbol +// write_memory - write 1,2,4 or 8 bytes to the +// specified memory space //------------------------------------------------- -void symbol_table::add(const char *name, read_write rw, u64 *ptr) +void symbol_table::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)); + address_space *tspace = &space; + + if (apply_translation) + { + // mask against the logical byte mask + address &= space.logaddrmask(); + + // translate if necessary; if not mapped, we're done + if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_WRITE, address, tspace)) + return; + } + + // otherwise, call the writing function for the translated address + switch (size) + { + case 1: tspace->write_byte(address, data); break; + case 2: tspace->write_word_unaligned(address, data); break; + case 4: tspace->write_dword_unaligned(address, data); break; + case 8: tspace->write_qword_unaligned(address, data); break; + } + + notify_memory_modified(); } //------------------------------------------------- -// add - add a new value symbol +// expression_get_space - return a space +// based on a case insensitive tag search //------------------------------------------------- -void symbol_table::add(const char *name, u64 value) +expression_error symbol_table::expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)); + device_t *device = nullptr; + std::string spacename; + if (tag) + { + // convert to lowercase then lookup the name (tags are enforced to be all lower case) + auto base = get_device_search(m_machine, m_memintf, tag); + device = base.first.subdevice(strmakelower(base.second)); + + // if that failed, treat the last component as an address space + if (!device) + { + std::string_view t = base.second; + auto const delimiter = t.find_last_of(":^"); + bool const found = std::string_view::npos != delimiter; + if (!found || (':' == t[delimiter])) + { + spacename = strmakelower(t.substr(found ? (delimiter + 1) : 0)); + t = t.substr(0, !found ? 0 : !delimiter ? 1 : delimiter); + if (!t.empty()) + device = base.first.subdevice(strmakelower(t)); + else + device = m_memintf ? &m_memintf->device() : &m_machine.root_device(); + } + } + } + else if (m_memintf) + { + device = &m_memintf->device(); + } + + // if still no device, report error + if (!device) + { + memory = nullptr; + return expression_error::INVALID_MEMORY_NAME; + } + + // ensure device has memory interface, and check for space if search not required + if (!device->interface(memory) || (spacename.empty() && (0 <= spacenum) && !memory->has_space(spacenum))) + { + memory = nullptr; + return expression_error::NO_SUCH_MEMORY_SPACE; + } + + // search not required + if (spacename.empty() && (0 <= spacenum)) + return expression_error::NONE; + + // find space by name or take first populated space if required + for (int i = 0; memory->max_space_count() > i; ++i) + { + if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename))) + { + spacenum = i; + return expression_error::NONE; + } + } + + // space not found + memory = nullptr; + return expression_error::NO_SUCH_MEMORY_SPACE; } //------------------------------------------------- -// add - add a new register symbol +// notify_memory_modified - notify that memory +// has been changed //------------------------------------------------- -void symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) +void symbol_table::notify_memory_modified() { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)); + // walk up the table hierarchy to find the owner + for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + if (symtable->m_memory_modified) + symtable->m_memory_modified(); } //------------------------------------------------- -// add - add a new function symbol +// memory_value - read 1,2,4 or 8 bytes at the +// given offset in the given address space //------------------------------------------------- -void symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) +u64 symbol_table::memory_value(const char *name, expression_space spacenum, u32 address, int size, bool disable_se) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)); + device_memory_interface *memory = m_memintf; + + bool logical = true; + int space = -1; + switch (spacenum) + { + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL)); + logical = false; + [[fallthrough]]; + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL); + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + return read_memory(memory->space(space), address, size, logical); + } + break; + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + return read_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size); + } + break; + + case EXPSPACE_REGION: + if (name) + return read_memory_region(name, address, size); + break; + + default: + break; + } + + return 0; } //------------------------------------------------- -// find_deep - do a deep search for a symbol, -// looking in the parent if needed +// read_program_direct - read memory directly +// from an opcode or RAM pointer //------------------------------------------------- -symbol_entry *symbol_table::find_deep(const char *symbol) +u64 symbol_table::read_program_direct(address_space &space, int opcode, offs_t address, int size) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + u8 *base; + + // adjust the address into a byte address, but not if being called recursively + if ((opcode & 2) == 0) + address = space.address_to_byte(address); + + // call ourself recursively until we are byte-sized + if (size > 1) { - symbol_entry *entry = symtable->find(symbol); - if (entry != nullptr) - return entry; + int halfsize = size / 2; + + // read each half, from lower address to upper address + u64 r0 = read_program_direct(space, opcode | 2, address + 0, halfsize); + u64 r1 = read_program_direct(space, opcode | 2, address + halfsize, halfsize); + + // assemble based on the target endianness + if (space.endianness() == ENDIANNESS_LITTLE) + return r0 | (r1 << (8 * halfsize)); + else + return r1 | (r0 << (8 * halfsize)); } - return nullptr; + + // handle the byte-sized final requests + else + { + // lowmask specified which address bits are within the databus width + offs_t lowmask = space.data_width() / 8 - 1; + + // get the base of memory, aligned to the address minus the lowbits + base = (u8 *)space.get_read_ptr(address & ~lowmask); + + // if we have a valid base, return the appropriate byte + if (base != nullptr) + { + if (space.endianness() == ENDIANNESS_LITTLE) + return base[BYTE8_XOR_LE(address) & lowmask]; + else + return base[BYTE8_XOR_BE(address) & lowmask]; + } + } + + return 0; } //------------------------------------------------- -// value - return the value of a symbol +// read_memory_region - read memory from a +// memory region //------------------------------------------------- -u64 symbol_table::value(const char *symbol) +u64 symbol_table::read_memory_region(const char *rgntag, offs_t address, int size) { - symbol_entry *entry = find_deep(symbol); - return (entry != nullptr) ? entry->value() : 0; + auto search = get_device_search(m_machine, m_memintf, rgntag); + memory_region *const region = search.first.memregion(search.second); + u64 result = ~u64(0) >> (64 - 8*size); + + // make sure we get a valid base before proceeding + if (region) + { + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + u64 r0, r1; + + // read each half, from lower address to upper address + r0 = read_memory_region(rgntag, address + 0, halfsize); + r1 = read_memory_region(rgntag, address + halfsize, halfsize); + + // assemble based on the target endianness + if (region->endianness() == ENDIANNESS_LITTLE) + result = r0 | (r1 << (8 * halfsize)); + else + result = r1 | (r0 << (8 * halfsize)); + } + + // only process if we're within range + else if (address < region->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = region->bytewidth() - 1; + u8 *base = region->base() + (address & ~lowmask); + + // if we have a valid base, return the appropriate byte + if (region->endianness() == ENDIANNESS_LITTLE) + result = base[BYTE8_XOR_LE(address) & lowmask]; + else + result = base[BYTE8_XOR_BE(address) & lowmask]; + } + } + return result; } //------------------------------------------------- -// set_value - set the value of a symbol +// set_memory_value - write 1,2,4 or 8 bytes at +// the given offset in the given address space //------------------------------------------------- -void symbol_table::set_value(const char *symbol, u64 value) +void symbol_table::set_memory_value(const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se) { - symbol_entry *entry = find_deep(symbol); - if (entry != nullptr) - entry->set_value(value); + device_memory_interface *memory = m_memintf; + + bool logical = true; + int space = -1; + switch (spacenum) + { + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL)); + logical = false; + [[fallthrough]]; + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL); + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + write_memory(memory->space(space), address, data, size, logical); + } + break; + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + write_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size, data); + } + break; + + case EXPSPACE_REGION: + if (name) + write_memory_region(name, address, size, data); + break; + + default: + break; + } } //------------------------------------------------- -// memory_valid - return true if the given -// memory name/space/offset combination is valid +// write_program_direct - write memory directly +// to an opcode or RAM pointer //------------------------------------------------- -expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space) +void symbol_table::write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + // adjust the address into a byte address, but not if being called recursively + if ((opcode & 2) == 0) + address = space.address_to_byte(address); + + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + + // break apart based on the target endianness + u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); + u64 r0, r1; + if (space.endianness() == ENDIANNESS_LITTLE) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE) - return err; + r0 = data & halfmask; + r1 = (data >> (8 * halfsize)) & halfmask; } - return expression_error::NO_SUCH_MEMORY_SPACE; + else + { + r0 = (data >> (8 * halfsize)) & halfmask; + r1 = data & halfmask; + } + + // write each half, from lower address to upper address + write_program_direct(space, opcode | 2, address + 0, halfsize, r0); + write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1); + } + + // handle the byte-sized final case + else + { + // lowmask specified which address bits are within the databus width + offs_t lowmask = space.data_width() / 8 - 1; + + // get the base of memory, aligned to the address minus the lowbits + u8 *base = (u8 *)space.get_read_ptr(address & ~lowmask); + + // if we have a valid base, write the appropriate byte + if (base != nullptr) + { + if (space.endianness() == ENDIANNESS_LITTLE) + base[BYTE8_XOR_LE(address) & lowmask] = data; + else + base[BYTE8_XOR_BE(address) & lowmask] = data; + notify_memory_modified(); + } + } } //------------------------------------------------- -// memory_value - return a value read from memory +// write_memory_region - write memory to a +// memory region //------------------------------------------------- -u64 symbol_table::memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se) +void symbol_table::write_memory_region(const char *rgntag, offs_t address, int size, u64 data) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + auto search = get_device_search(m_machine, m_memintf, rgntag); + memory_region *const region = search.first.memregion(search.second); + + // make sure we get a valid base before proceeding + if (region) + { + // call ourself recursively until we are byte-sized + if (size > 1) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_read != nullptr) - return symtable->m_memory_read(symtable->m_memory_param, name, space, offset, size, disable_se); - return 0; + int halfsize = size / 2; + + // break apart based on the target endianness + u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); + u64 r0, r1; + if (region->endianness() == ENDIANNESS_LITTLE) + { + r0 = data & halfmask; + r1 = (data >> (8 * halfsize)) & halfmask; + } + else + { + r0 = (data >> (8 * halfsize)) & halfmask; + r1 = data & halfmask; + } + + // write each half, from lower address to upper address + write_memory_region(rgntag, address + 0, halfsize, r0); + write_memory_region(rgntag, address + halfsize, halfsize, r1); } - return 0; + + // only process if we're within range + else if (address < region->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = region->bytewidth() - 1; + u8 *base = region->base() + (address & ~lowmask); + + // if we have a valid base, set the appropriate byte + if (region->endianness() == ENDIANNESS_LITTLE) + { + base[BYTE8_XOR_LE(address) & lowmask] = data; + } + else + { + base[BYTE8_XOR_BE(address) & lowmask] = data; + } + notify_memory_modified(); + } + } } //------------------------------------------------- -// set_memory_value - write a value to memory +// memory_valid - return true if the given +// memory name/space/offset combination is valid //------------------------------------------------- -void symbol_table::set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se) +expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + device_memory_interface *memory = m_memintf; + + int spaceno = -1; + switch (space) + { + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL); + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL); + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + spaceno = (space == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_REGION: + if (!name) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_write != nullptr) - symtable->m_memory_write(symtable->m_memory_param, name, space, offset, size, value, disable_se); - return; + return expression_error::MISSING_MEMORY_NAME; } + else + { + auto search = get_device_search(m_machine, m_memintf, name); + memory_region *const region = search.first.memregion(search.second); + if (!region || !region->base()) + return expression_error::INVALID_MEMORY_NAME; + } + break; + + default: + return expression_error::NO_SUCH_MEMORY_SPACE; + } + + return expression_error::NONE; } @@ -538,16 +1014,33 @@ void symbol_table::set_memory_value(const char *name, expression_space space, u3 // parsed_expression - constructor //------------------------------------------------- -parsed_expression::parsed_expression(symbol_table *symtable, const char *expression, u64 *result) +parsed_expression::parsed_expression(symbol_table &symtable) : m_symtable(symtable) + , m_default_base(16) { - // if we got an expression parse it - if (expression != nullptr) - parse(expression); +} + +parsed_expression::parsed_expression(symbol_table &symtable, std::string_view expression, int default_base) + : m_symtable(symtable) + , m_default_base(default_base) +{ + assert(default_base == 8 || default_base == 10 || default_base == 16); + + parse(expression); +} + - // if we get a result pointer, execute it - if (result != nullptr) - *result = execute(); +//------------------------------------------------- +// parsed_expression - copy constructor +//------------------------------------------------- + +parsed_expression::parsed_expression(const parsed_expression &src) + : m_symtable(src.m_symtable) + , m_default_base(src.m_default_base) + , m_original_string(src.m_original_string) +{ + if (!m_original_string.empty()) + parse_string_into_tokens(); } @@ -555,12 +1048,12 @@ parsed_expression::parsed_expression(symbol_table *symtable, const char *express // parse - parse an expression into tokens //------------------------------------------------- -void parsed_expression::parse(const char *expression) +void parsed_expression::parse(std::string_view expression) { // copy the string and reset our parsing state m_original_string.assign(expression); - m_tokenlist.reset(); - m_stringlist.reset(); + m_tokenlist.clear(); + m_stringlist.clear(); // first parse the tokens into the token array in order parse_string_into_tokens(); @@ -577,6 +1070,7 @@ void parsed_expression::parse(const char *expression) void parsed_expression::copy(const parsed_expression &src) { m_symtable = src.m_symtable; + m_default_base = src.m_default_base; m_original_string.assign(src.m_original_string); if (!m_original_string.empty()) parse_string_into_tokens(); @@ -588,89 +1082,72 @@ void parsed_expression::copy(const parsed_expression &src) // human readable token representation //------------------------------------------------- -void parsed_expression::print_tokens(FILE *out) +void parsed_expression::print_tokens() { -#if DEBUG_TOKENS - osd_printf_debug("----\n"); - for (parse_token *token = m_tokens.first(); token != nullptr; token = token->next()) + LOG("----\n"); + for (parse_token &token : m_tokenlist) { - switch (token->type) + if (token.is_number()) + LOG("NUMBER: %016X\n", token.value()); + else if (token.is_string()) + LOG("STRING: ""%s""\n", token.string()); + else if (token.is_symbol()) + LOG("SYMBOL: %s%s%s\n", token.symbol().name(), token.symbol().is_function() ? "()" : "", token.symbol().is_lval() ? " &" : ""); + else if (token.is_operator()) { - default: - case parse_token::INVALID: - fprintf(out, "INVALID\n"); - break; - - case parse_token::END: - fprintf(out, "END\n"); - break; - - case parse_token::NUMBER: - fprintf(out, "NUMBER: %08X%08X\n", (u32)(token->value.i >> 32), u32(token->value.i)); - break; - - case parse_token::STRING: - fprintf(out, "STRING: ""%s""\n", token->string); - break; - - case parse_token::SYMBOL: - fprintf(out, "SYMBOL: %08X%08X\n", u32(token->value.i >> 32), u32(token->value.i)); - break; - - case parse_token::OPERATOR: - switch (token->value.i) - { - case TVL_LPAREN: fprintf(out, "(\n"); break; - case TVL_RPAREN: fprintf(out, ")\n"); break; - case TVL_PLUSPLUS: fprintf(out, "++ (unspecified)\n"); break; - case TVL_MINUSMINUS: fprintf(out, "-- (unspecified)\n"); break; - case TVL_PREINCREMENT: fprintf(out, "++ (prefix)\n"); break; - case TVL_PREDECREMENT: fprintf(out, "-- (prefix)\n"); break; - case TVL_POSTINCREMENT: fprintf(out, "++ (postfix)\n"); break; - case TVL_POSTDECREMENT: fprintf(out, "-- (postfix)\n"); break; - case TVL_COMPLEMENT: fprintf(out, "!\n"); break; - case TVL_NOT: fprintf(out, "~\n"); break; - case TVL_UPLUS: fprintf(out, "+ (unary)\n"); break; - case TVL_UMINUS: fprintf(out, "- (unary)\n"); break; - case TVL_MULTIPLY: fprintf(out, "*\n"); break; - case TVL_DIVIDE: fprintf(out, "/\n"); break; - case TVL_MODULO: fprintf(out, "%%\n"); break; - case TVL_ADD: fprintf(out, "+\n"); break; - case TVL_SUBTRACT: fprintf(out, "-\n"); break; - case TVL_LSHIFT: fprintf(out, "<<\n"); break; - case TVL_RSHIFT: fprintf(out, ">>\n"); break; - case TVL_LESS: fprintf(out, "<\n"); break; - case TVL_LESSOREQUAL: fprintf(out, "<=\n"); break; - case TVL_GREATER: fprintf(out, ">\n"); break; - case TVL_GREATEROREQUAL:fprintf(out, ">=\n"); break; - case TVL_EQUAL: fprintf(out, "==\n"); break; - case TVL_NOTEQUAL: fprintf(out, "!=\n"); break; - case TVL_BAND: fprintf(out, "&\n"); break; - case TVL_BXOR: fprintf(out, "^\n"); break; - case TVL_BOR: fprintf(out, "|\n"); break; - case TVL_LAND: fprintf(out, "&&\n"); break; - case TVL_LOR: fprintf(out, "||\n"); break; - case TVL_ASSIGN: fprintf(out, "=\n"); break; - case TVL_ASSIGNMULTIPLY:fprintf(out, "*=\n"); break; - case TVL_ASSIGNDIVIDE: fprintf(out, "/=\n"); break; - case TVL_ASSIGNMODULO: fprintf(out, "%%=\n"); break; - case TVL_ASSIGNADD: fprintf(out, "+=\n"); break; - case TVL_ASSIGNSUBTRACT:fprintf(out, "-=\n"); break; - case TVL_ASSIGNLSHIFT: fprintf(out, "<<=\n"); break; - case TVL_ASSIGNRSHIFT: fprintf(out, ">>=\n"); break; - case TVL_ASSIGNBAND: fprintf(out, "&=\n"); break; - case TVL_ASSIGNBXOR: fprintf(out, "^=\n"); break; - case TVL_ASSIGNBOR: fprintf(out, "|=\n"); break; - case TVL_COMMA: fprintf(out, ",\n"); break; - case TVL_MEMORYAT: fprintf(out, token.memory_size_effect() ? "mem!\n" : "mem@\n");break; - case TVL_EXECUTEFUNC: fprintf(out, "execute\n"); break; - default: fprintf(out, "INVALID OPERATOR\n"); break; - } - break; + switch (token.optype()) + { + case TVL_LPAREN: LOG("(\n"); break; + case TVL_RPAREN: LOG(")\n"); break; + case TVL_PLUSPLUS: LOG("++ (unspecified)\n"); break; + case TVL_MINUSMINUS: LOG("-- (unspecified)\n"); break; + case TVL_PREINCREMENT: LOG("++ (prefix)\n"); break; + case TVL_PREDECREMENT: LOG("-- (prefix)\n"); break; + case TVL_POSTINCREMENT: LOG("++ (postfix)\n"); break; + case TVL_POSTDECREMENT: LOG("-- (postfix)\n"); break; + case TVL_COMPLEMENT: LOG("!\n"); break; + case TVL_NOT: LOG("~\n"); break; + case TVL_UPLUS: LOG("+ (unary)\n"); break; + case TVL_UMINUS: LOG("- (unary)\n"); break; + case TVL_MULTIPLY: LOG("*\n"); break; + case TVL_DIVIDE: LOG("/\n"); break; + case TVL_MODULO: LOG("%%\n"); break; + case TVL_ADD: LOG("+\n"); break; + case TVL_SUBTRACT: LOG("-\n"); break; + case TVL_LSHIFT: LOG("<<\n"); break; + case TVL_RSHIFT: LOG(">>\n"); break; + case TVL_LESS: LOG("<\n"); break; + case TVL_LESSOREQUAL: LOG("<=\n"); break; + case TVL_GREATER: LOG(">\n"); break; + case TVL_GREATEROREQUAL:LOG(">=\n"); break; + case TVL_EQUAL: LOG("==\n"); break; + case TVL_NOTEQUAL: LOG("!=\n"); break; + case TVL_BAND: LOG("&\n"); break; + case TVL_BXOR: LOG("^\n"); break; + case TVL_BOR: LOG("|\n"); break; + case TVL_LAND: LOG("&&\n"); break; + case TVL_LOR: LOG("||\n"); break; + case TVL_ASSIGN: LOG("=\n"); break; + case TVL_ASSIGNMULTIPLY:LOG("*=\n"); break; + case TVL_ASSIGNDIVIDE: LOG("/=\n"); break; + case TVL_ASSIGNMODULO: LOG("%%=\n"); break; + case TVL_ASSIGNADD: LOG("+=\n"); break; + case TVL_ASSIGNSUBTRACT:LOG("-=\n"); break; + case TVL_ASSIGNLSHIFT: LOG("<<=\n"); break; + case TVL_ASSIGNRSHIFT: LOG(">>=\n"); break; + case TVL_ASSIGNBAND: LOG("&=\n"); break; + case TVL_ASSIGNBXOR: LOG("^=\n"); break; + case TVL_ASSIGNBOR: LOG("|=\n"); break; + case TVL_COMMA: LOG(",\n"); break; + case TVL_MEMORYAT: LOG(token.memory_side_effects() ? "mem!\n" : "mem@\n");break; + case TVL_EXECUTEFUNC: LOG("execute\n"); break; + default: LOG("INVALID OPERATOR\n"); break; + } } + else + LOG("INVALID\n"); } - osd_printf_debug("----\n"); -#endif + LOG("----\n"); } @@ -693,7 +1170,8 @@ void parsed_expression::parse_string_into_tokens() break; // initialize the current token object - parse_token &token = m_tokenlist.append(*global_alloc(parse_token(string - stringstart))); + m_tokenlist.emplace_back(string - stringstart); + parse_token &token = m_tokenlist.back(); // switch off the first character switch (tolower(u8(string[0]))) @@ -854,12 +1332,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& // check for memory @ and ! operators if (string[0] == '@' || string[0] == '!') { - try { + try + { bool disable_se = string[0] == '@'; parse_memory_operator(token, buffer.c_str(), disable_se); string += 1; return; - } catch(const expression_error &) { + } + catch (const expression_error &) + { // Try some other operator instead } } @@ -943,21 +1424,20 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& catch (expression_error const &err) { // this is really a hack, but 0B1234 could also hex depending on default base - if (expression_error::INVALID_NUMBER == err) - return parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::INVALID_NUMBER); + if (expression_error::INVALID_NUMBER == err && m_default_base == 16) + return parse_number(token, buffer.c_str(), m_default_base, expression_error::INVALID_NUMBER); else throw; } - // TODO: for octal address spaces, treat 0123 as octal default: ; // fall through } - // fall through + [[fallthrough]]; default: // check for a symbol match - symbol_entry *symbol = m_symtable->find_deep(buffer.c_str()); + symbol_entry *symbol = m_symtable.get().find_deep(buffer.c_str()); if (symbol != nullptr) { token.configure_symbol(*symbol); @@ -965,14 +1445,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& // if this is a function symbol, synthesize an execute function operator if (symbol->is_function()) { - parse_token &newtoken = m_tokenlist.append(*global_alloc(parse_token(string - stringstart))); + m_tokenlist.emplace_back(string - stringstart); + parse_token &newtoken = m_tokenlist.back(); newtoken.configure_operator(TVL_EXECUTEFUNC, 0); } return; } // attempt to parse as a number in the default base - parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::UNKNOWN_SYMBOL); + parse_number(token, buffer.c_str(), m_default_base, expression_error::UNKNOWN_SYMBOL); } } @@ -1072,7 +1553,7 @@ void parsed_expression::parse_quoted_string(parse_token &token, const char *&str string++; // make the token - token.configure_string(m_stringlist.append(*global_alloc(expression_string(buffer.c_str())))); + token.configure_string(m_stringlist.emplace(m_stringlist.end(), buffer.c_str())->c_str()); } @@ -1089,17 +1570,17 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st const char *dot = strrchr(string, '.'); if (dot != nullptr) { - namestring = m_stringlist.append(*global_alloc(expression_string(string, dot - string))); + namestring = m_stringlist.emplace(m_stringlist.end(), string, dot)->c_str(); string = dot + 1; } - // length 3 means logical/physical, then space, then size int length = (int)strlen(string); bool physical = false; int space = 'p'; int size; if (length == 3) { + // length 3 means logical/physical, then space, then size if (string[0] != 'l' && string[0] != 'p') throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring)); if (string[1] != 'p' && string[1] != 'd' && string[1] != 'i' && string[1] != '3') @@ -1108,21 +1589,22 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st space = string[1]; size = string[2]; } - - // length 2 means space then size else if (length == 2) { + // length 2 means space then size space = string[0]; size = string[1]; } - - // length 1 means size else if (length == 1) + { + // length 1 means size size = string[0]; - - // anything else is invalid + } else + { + // anything else is invalid throw expression_error(expression_error::INVALID_TOKEN, token.offset()); + } // convert the space to flags expression_space memspace; @@ -1131,9 +1613,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st case 'p': memspace = physical ? EXPSPACE_PROGRAM_PHYSICAL : EXPSPACE_PROGRAM_LOGICAL; break; case 'd': memspace = physical ? EXPSPACE_DATA_PHYSICAL : EXPSPACE_DATA_LOGICAL; break; case 'i': memspace = physical ? EXPSPACE_IO_PHYSICAL : EXPSPACE_IO_LOGICAL; break; - case '3': memspace = physical ? EXPSPACE_SPACE3_PHYSICAL : EXPSPACE_SPACE3_LOGICAL; break; - case 'o': memspace = EXPSPACE_OPCODE; break; - case 'r': memspace = EXPSPACE_RAMWRITE; break; + case '3': memspace = physical ? EXPSPACE_OPCODE_PHYSICAL : EXPSPACE_OPCODE_LOGICAL; break; + case 'r': memspace = EXPSPACE_PRGDIRECT; break; + case 'o': memspace = EXPSPACE_OPDIRECT; break; case 'm': memspace = EXPSPACE_REGION; break; default: throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring)); } @@ -1150,12 +1632,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st } // validate the name - if (m_symtable != nullptr) - { - expression_error::error_code err = m_symtable->memory_valid(namestring, memspace); - if (err != expression_error::NONE) - throw expression_error(err, token.offset() + (string - startstring)); - } + expression_error::error_code err = m_symtable.get().memory_valid(namestring, memspace); + if (err != expression_error::NONE) + throw expression_error(err, token.offset() + (string - startstring)); // configure the token token.configure_operator(TVL_MEMORYAT, 2).set_memory_size(memsize).set_memory_space(memspace).set_memory_source(namestring).set_memory_side_effects(disable_se); @@ -1167,9 +1646,8 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st // ambiguities based on neighboring tokens //------------------------------------------------- -void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &thistoken) +void parsed_expression::normalize_operator(parse_token &thistoken, parse_token *prevtoken, parse_token *nexttoken, const std::list<parse_token> &stack, bool was_rparen) { - parse_token *nexttoken = thistoken.next(); switch (thistoken.optype()) { // Determine if an open paren is part of a function or not @@ -1203,15 +1681,15 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token & case TVL_SUBTRACT: // Assume we're unary if we are the first token, or if the previous token is not // a symbol, a number, or a right parenthesis - if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !prevtoken->is_operator(TVL_RPAREN))) + if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !was_rparen)) thistoken.configure_operator(thistoken.is_operator(TVL_ADD) ? TVL_UPLUS : TVL_UMINUS, 2); break; // Determine if , refers to a function parameter case TVL_COMMA: - for (auto lookback = m_token_stack.rbegin(); lookback != m_token_stack.rend(); ++lookback) + for (auto lookback = stack.begin(); lookback != stack.end(); ++lookback) { - parse_token &peek = *lookback; + const parse_token &peek = *lookback; // if we hit an execute function operator, or else a left parenthesis that is // already tagged, then tag us as well @@ -1233,51 +1711,63 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token & void parsed_expression::infix_to_postfix() { - simple_list<parse_token> stack; + std::list<parse_token> stack; + parse_token *prev = nullptr; + + // this flag is used to avoid looking back at a closing parenthesis that was already destroyed + bool was_rparen = false; // loop over all the original tokens - parse_token *prev = nullptr; - parse_token *next; - for (parse_token *token = m_tokenlist.detach_all(); token != nullptr; prev = token, token = next) + std::list<parse_token>::iterator next; + std::list<parse_token> origlist = std::move(m_tokenlist); + m_tokenlist.clear(); + for (std::list<parse_token>::iterator token = origlist.begin(); token != origlist.end(); token = next) { // pre-determine our next token - next = token->next(); + next = std::next(token); // if the character is an operand, append it to the result string if (token->is_number() || token->is_symbol() || token->is_string()) - m_tokenlist.append(*token); + { + m_tokenlist.splice(m_tokenlist.end(), origlist, token); + + // remember this as the previous token + prev = &*token; + was_rparen = false; + } // if this is an operator, process it else if (token->is_operator()) { // normalize the operator based on neighbors - normalize_operator(prev, *token); + normalize_operator(*token, prev, next != origlist.end() ? &*next : nullptr, stack, was_rparen); + was_rparen = false; // if the token is an opening parenthesis, push it onto the stack. if (token->is_operator(TVL_LPAREN)) - stack.prepend(*token); + stack.splice(stack.begin(), origlist, token); // if the token is a closing parenthesis, pop all operators until we // reach an opening parenthesis and append them to the result string, - // discaring the open parenthesis + // discarding the open parenthesis else if (token->is_operator(TVL_RPAREN)) { - // loop until we find our matching opener - parse_token *popped; - while ((popped = stack.detach_head()) != nullptr) - { - if (popped->is_operator(TVL_LPAREN)) - break; - m_tokenlist.append(*popped); - } + // find our matching opener + std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(), + [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); } + ); // if we didn't find an open paren, it's an error - if (popped == nullptr) + if (lparen == stack.end()) throw expression_error(expression_error::UNBALANCED_PARENS, token->offset()); + // move the stacked operators to the end of the new list + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), lparen); + // free ourself and our matching opening parenthesis - global_free(token); - global_free(popped); + origlist.erase(token); + stack.erase(lparen); + was_rparen = true; } // if the token is an operator, pop operators until we reach an opening parenthesis, @@ -1288,8 +1778,8 @@ void parsed_expression::infix_to_postfix() int our_precedence = token->precedence(); // loop until we can't peek at the stack anymore - parse_token *peek; - while ((peek = stack.first()) != nullptr) + std::list<parse_token>::iterator peek; + for (peek = stack.begin(); peek != stack.end(); ++peek) { // break if any of the above conditions are true if (peek->is_operator(TVL_LPAREN)) @@ -1297,28 +1787,29 @@ void parsed_expression::infix_to_postfix() int stack_precedence = peek->precedence(); if (stack_precedence > our_precedence || (stack_precedence == our_precedence && peek->right_to_left())) break; - - // pop this token - m_tokenlist.append(*stack.detach_head()); } + // move the stacked operands to the end of the new list + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), peek); + // push the new operator - stack.prepend(*token); + stack.splice(stack.begin(), origlist, token); } + + if (!was_rparen) + prev = &*token; } } - // finish popping the stack - parse_token *popped; - while ((popped = stack.detach_head()) != nullptr) - { - // it is an error to have a left parenthesis still on the stack - if (popped->is_operator(TVL_LPAREN)) - throw expression_error(expression_error::UNBALANCED_PARENS, popped->offset()); + // it is an error to have a left parenthesis still on the stack + std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(), + [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); } + ); + if (lparen != stack.end()) + throw expression_error(expression_error::UNBALANCED_PARENS, lparen->offset()); - // pop this token - m_tokenlist.append(*popped); - } + // pop all remaining tokens + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), stack.end()); } @@ -1664,8 +2155,7 @@ u64 parsed_expression::execute_tokens() //------------------------------------------------- parsed_expression::parse_token::parse_token(int offset) - : m_next(nullptr), - m_type(INVALID), + : m_type(INVALID), m_offset(offset), m_value(0), m_flags(0), @@ -1680,16 +2170,15 @@ parsed_expression::parse_token::parse_token(int offset) // for a SYMBOL token //------------------------------------------------- -u64 parsed_expression::parse_token::get_lval_value(symbol_table *table) +u64 parsed_expression::parse_token::get_lval_value(symbol_table &table) { // get the value of a symbol if (is_symbol()) return m_symbol->value(); // or get the value from the memory callbacks - else if (is_memory() && table != nullptr) { - return table->memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects()); - } + else if (is_memory()) + return table.memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects()); return 0; } @@ -1700,15 +2189,15 @@ u64 parsed_expression::parse_token::get_lval_value(symbol_table *table) // for a SYMBOL token //------------------------------------------------- -inline void parsed_expression::parse_token::set_lval_value(symbol_table *table, u64 value) +inline void parsed_expression::parse_token::set_lval_value(symbol_table &table, u64 value) { // set the value of a symbol if (is_symbol()) m_symbol->set_value(value); // or set the value via the memory callbacks - else if (is_memory() && table != nullptr) - table->set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects()); + else if (is_memory()) + table.set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects()); } @@ -1733,7 +2222,7 @@ void parsed_expression::execute_function(parse_token &token) // if it is a function symbol, break out of the loop if (peek.is_symbol()) { - symbol = peek.symbol(); + symbol = &peek.symbol(); if (symbol->is_function()) { m_token_stack.pop_back(); @@ -1751,8 +2240,14 @@ void parsed_expression::execute_function(parse_token &token) if (paramcount == MAX_FUNCTION_PARAMS) throw expression_error(expression_error::INVALID_PARAM_COUNT, token.offset()); - // execute the function and push the result + // validate parameters function_symbol_entry *function = downcast<function_symbol_entry *>(symbol); + if (paramcount < function->minparams()) + throw expression_error(expression_error::TOO_FEW_PARAMS, token.offset(), function->minparams()); + if (paramcount > function->maxparams()) + throw expression_error(expression_error::TOO_MANY_PARAMS, token.offset(), function->maxparams()); + + // execute the function and push the result parse_token result(token.offset()); result.configure_number(function->execute(paramcount, &funcparams[MAX_FUNCTION_PARAMS - paramcount])); push_token(result); |