summaryrefslogtreecommitdiffstats
path: root/src/emu/debug/express.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/debug/express.cpp')
-rw-r--r--src/emu/debug/express.cpp594
1 files changed, 557 insertions, 37 deletions
diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp
index bb1f17e05f3..2f5da8d0dea 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.
@@ -358,24 +358,23 @@ u64 function_symbol_entry::execute(int numparams, const u64 *paramlist)
// symbol_table - constructor
//-------------------------------------------------
-symbol_table::symbol_table(symbol_table *parent)
- : m_parent(parent),
- m_memory_valid(nullptr),
- m_memory_read(nullptr),
- m_memory_write(nullptr)
+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)
{
}
//-------------------------------------------------
-// add - add a new u64 pointer symbol
+// set_memory_modified_func - install notifier
+// for when memory is modified in debugger
//-------------------------------------------------
-void symbol_table::configure_memory(valid_func valid, read_func read, write_func write)
+void symbol_table::set_memory_modified_func(memory_modified_func modified)
{
- m_memory_valid = std::move(valid);
- m_memory_read = std::move(read);
- m_memory_write = std::move(write);
+ m_memory_modified = std::move(modified);
}
@@ -464,59 +463,580 @@ void symbol_table::set_value(const char *symbol, u64 value)
}
+
+//**************************************************************************
+// EXPRESSION MEMORY HANDLERS
+//**************************************************************************
+
//-------------------------------------------------
-// memory_valid - return true if the given
-// memory name/space/offset combination is valid
+// read_memory - return 1,2,4 or 8 bytes
+// from the specified memory space
//-------------------------------------------------
-expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space)
+u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bool apply_translation)
+{
+ u64 result = ~u64(0) >> (64 - 8*size);
+
+ if (apply_translation)
+ {
+ // mask against the logical byte mask
+ address &= space.logaddrmask();
+
+ // translate if necessary; if not mapped, return 0xffffffffffffffff
+ if (!space.device().memory().translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
+ return result;
+ }
+
+ // otherwise, call the reading function for the translated address
+ switch (size)
+ {
+ case 1: result = space.read_byte(address); break;
+ case 2: result = space.read_word_unaligned(address); break;
+ case 4: result = space.read_dword_unaligned(address); break;
+ case 8: result = space.read_qword_unaligned(address); break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_memory - write 1,2,4 or 8 bytes to the
+// specified memory space
+//-------------------------------------------------
+
+void symbol_table::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation)
+{
+ 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(), TRANSLATE_WRITE_DEBUG, address))
+ return;
+ }
+
+ // otherwise, call the writing function for the translated address
+ switch (size)
+ {
+ case 1: space.write_byte(address, data); break;
+ case 2: space.write_word_unaligned(address, data); break;
+ case 4: space.write_dword_unaligned(address, data); break;
+ case 8: space.write_qword_unaligned(address, data); break;
+ }
+
+ notify_memory_modified();
+}
+
+
+//-------------------------------------------------
+// expression_get_device - return a device
+// based on a case insensitive tag search
+//-------------------------------------------------
+
+device_t *symbol_table::expression_get_device(const char *tag)
+{
+ // convert to lowercase then lookup the name (tags are enforced to be all lower case)
+ std::string fullname(tag);
+ strmakelower(fullname);
+ return m_machine.root_device().subdevice(fullname.c_str());
+}
+
+
+//-------------------------------------------------
+// notify_memory_modified - notify that memory
+// has been changed
+//-------------------------------------------------
+
+void symbol_table::notify_memory_modified()
{
// 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)
+ if (symtable->m_memory_modified != nullptr)
+ m_memory_modified();
+}
+
+
+//-------------------------------------------------
+// memory_value - read 1,2,4 or 8 bytes at the
+// given offset in the given address space
+//-------------------------------------------------
+
+u64 symbol_table::memory_value(const char *name, expression_space spacenum, u32 address, int size, bool disable_se)
+{
+ device_memory_interface *memory = m_memintf;
+
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != nullptr)
{
- expression_error::error_code err = symtable->m_memory_valid(name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE)
- return err;
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
}
- return expression_error::NO_SUCH_MEMORY_SPACE;
+ if (memory != nullptr && memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)))
+ {
+ address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_memory(space, address, size, true);
+ }
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)))
+ {
+ address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_memory(space, address, size, false);
+ }
+ break;
+
+ case EXPSPACE_RAMWRITE:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_PROGRAM))
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size);
+ }
+ break;
+
+ case EXPSPACE_OPCODE:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_OPCODES))
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ return read_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size);
+ }
+ break;
+
+ case EXPSPACE_REGION:
+ if (name == nullptr)
+ break;
+ return read_memory_region(name, address, size);
+
+ default:
+ break;
+ }
+
+ return 0;
}
//-------------------------------------------------
-// memory_value - return a value read from memory
+// read_program_direct - read memory directly
+// from an opcode or RAM pointer
//-------------------------------------------------
-u64 symbol_table::memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se)
+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)
- if (symtable->m_memory_valid != nullptr)
+ 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)
+ {
+ 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));
+ }
+
+ // 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)
{
- expression_error::error_code err = symtable->m_memory_valid(name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_read != nullptr)
- return symtable->m_memory_read(name, space, offset, size, disable_se);
- return 0;
+ if (space.endianness() == ENDIANNESS_LITTLE)
+ return base[BYTE8_XOR_LE(address) & lowmask];
+ else
+ return base[BYTE8_XOR_BE(address) & lowmask];
}
+ }
+
return 0;
}
//-------------------------------------------------
-// set_memory_value - write a value to memory
+// read_memory_region - read memory from a
+// memory region
//-------------------------------------------------
-void symbol_table::set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se)
+u64 symbol_table::read_memory_region(const char *rgntag, 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)
- if (symtable->m_memory_valid != nullptr)
+ memory_region *region = m_machine.root_device().memregion(rgntag);
+ u64 result = ~u64(0) >> (64 - 8*size);
+
+ // make sure we get a valid base before proceeding
+ if (region != nullptr)
+ {
+ // call ourself recursively until we are byte-sized
+ if (size > 1)
{
- expression_error::error_code err = symtable->m_memory_valid(name, space);
- if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_write != nullptr)
- symtable->m_memory_write(name, space, offset, size, value, disable_se);
- return;
+ 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_memory_value - write 1,2,4 or 8 bytes at
+// the given offset in the given address space
+//-------------------------------------------------
+
+void symbol_table::set_memory_value(const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se)
+{
+ device_memory_interface *memory = m_memintf;
+
+ switch (spacenum)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)))
+ {
+ address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL));
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_memory(space, address, data, size, true);
+ }
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)))
+ {
+ address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL));
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_memory(space, address, data, size, false);
+ }
+ break;
+
+ case EXPSPACE_RAMWRITE: {
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_PROGRAM))
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size, data);
+ }
+ break;
+ }
+
+ case EXPSPACE_OPCODE: {
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device != nullptr)
+ device->interface(memory);
+ }
+ if (memory != nullptr && memory->has_space(AS_OPCODES))
+ {
+ auto dis = m_machine.disable_side_effects(disable_se);
+ write_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size, data);
+ }
+ break;
+ }
+
+ case EXPSPACE_REGION:
+ if (name == nullptr)
+ break;
+ write_memory_region(name, address, size, data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// write_program_direct - write memory directly
+// to an opcode or RAM pointer
+//-------------------------------------------------
+
+void symbol_table::write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data)
+{
+ // 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)
+ {
+ 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_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();
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// write_memory_region - write memory to a
+// memory region
+//-------------------------------------------------
+
+void symbol_table::write_memory_region(const char *rgntag, offs_t address, int size, u64 data)
+{
+ memory_region *region = m_machine.root_device().memregion(rgntag);
+
+ // make sure we get a valid base before proceeding
+ if (region != nullptr)
+ {
+ // 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 (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);
+ }
+
+ // 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();
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// memory_valid - return true if the given
+// memory name/space/offset combination is valid
+//-------------------------------------------------
+
+expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space)
+{
+ device_memory_interface *memory = m_memintf;
+
+ switch (space)
+ {
+ case EXPSPACE_PROGRAM_LOGICAL:
+ case EXPSPACE_DATA_LOGICAL:
+ case EXPSPACE_IO_LOGICAL:
+ case EXPSPACE_SPACE3_LOGICAL:
+ if (name != nullptr)
+ {
+ device_t *device = expression_get_device(name);
+ if (device == nullptr)
+ return expression_error::INVALID_MEMORY_NAME;
+ if (!device->interface(memory))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ else if (memory == nullptr)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (!memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL)))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_PROGRAM_PHYSICAL:
+ case EXPSPACE_DATA_PHYSICAL:
+ case EXPSPACE_IO_PHYSICAL:
+ case EXPSPACE_SPACE3_PHYSICAL:
+ if (name)
+ {
+ device_t *device = expression_get_device(name);
+ if (device == nullptr)
+ return expression_error::INVALID_MEMORY_NAME;
+ if (!device->interface(memory))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ else if (memory == nullptr)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (!memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL)))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_RAMWRITE:
+ if (name)
+ {
+ device_t *device = expression_get_device(name);
+ if (device == nullptr)
+ return expression_error::INVALID_MEMORY_NAME;
+ if (!device->interface(memory))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ else if (memory == nullptr)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (!memory->has_space(AS_PROGRAM))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_OPCODE:
+ if (name)
+ {
+ device_t *device = expression_get_device(name);
+ if (device == nullptr)
+ return expression_error::INVALID_MEMORY_NAME;
+ if (!device->interface(memory))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ else if (memory == nullptr)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (!memory->has_space(AS_OPCODES))
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ break;
+
+ case EXPSPACE_REGION:
+ if (!name)
+ return expression_error::MISSING_MEMORY_NAME;
+ if (!m_machine.root_device().memregion(name) || !m_machine.root_device().memregion(name)->base())
+ return expression_error::INVALID_MEMORY_NAME;
+ break;
+
+ default:
+ return expression_error::NO_SUCH_MEMORY_SPACE;
+ }
+ return expression_error::NONE;
}