diff options
Diffstat (limited to 'src/emu/debug/express.cpp')
-rw-r--r-- | src/emu/debug/express.cpp | 594 |
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; } |