The device_memory_interface

1. Capabilities

The device memory interface provides devices with the capability of creating address spaces, to which address maps can be associated. It’s used for any device that provides a (logical) address/data bus that other devices can be connected to. That’s mainly, but not solely, CPUs.

The interface allows for an unlimited set of address spaces, numbered with small, non-negative values. The IDs index vectors, so they should stay small to keep the lookup fast. Spaces numbered 0-3 have associated constant name:

ID

Name

0

AS_PROGRAM

1

AS_DATA

2

AS_IO

3

AS_OPCODES

Spaces 0 and 3, i.e. AS_PROGRAM and AS_OPCODES, are special for the debugger and some CPUs. AS_PROGRAM is use by the debugger and the CPUs as the space from which the CPU reads its instructions for the disassembler. When present, AS_OPCODES is used by the debugger and some CPUs to read the opcode part of the instruction. What opcode means is device-dependant, for instance for the Z80 it's the initial byte(s) which are read with the M1 signal asserted, while for the 68000 is means every instruction word plus PC-relative accesses. The main, but not only, use of AS_OPCODES is to implement hardware decryption of instructions separately from data.

2. Setup

std::vector<std::pair<int, const address_space_config *>> memory_space_config() const;

The device must override that method to provide a vector of pairs comprising of a space number and an associated address_space_config describing its configuration. Some examples to look up when needed:

bool has_configured_map(int index = 0) const;

The has_configured_map method allows to test whether an address_map has been associated with a given space in the memory_space_config method. That allows optional memory spaces to be implemented, such as AS_OPCODES in certain CPU cores.

3. Associating maps to spaces

Associating maps to spaces is done at the machine configuration level, after the device is instantiated.

void set_addrmap(int spacenum, T &obj, Ret (U::*func)(Params...));
void set_addrmap(int spacenum, Ret (T::*func)(Params...));
void set_addrmap(int spacenum, address_map_constructor map);

These function associate a map with a given space. Address maps associated with non-existent spaces are ignored (no warning given). The first form takes a reference to an object and a method to call on that object. The second form takes a method to call on the current device being configured. The third form takes an address_map_constructor to copy. In each case, the function must be callable with reference to an address_map object as an argument.

To remove a previously configured address map, call set_addrmap with a default-constructed address_map_constructor (useful for removing a map for an optional space in a derived machine configuration).

As an example, here’s the address map configuration for the main CPU in the Hana Yayoi and Hana Fubuki machines, with all distractions removed:

class hnayayoi_state : public driver_device
{
public:
    void hnayayoi(machine_config &config);
    void hnfubuki(machine_config &config);

private:
    required_device<cpu_device> m_maincpu;

    void hnayayoi_map(address_map &map);
    void hnayayoi_io_map(address_map &map);
    void hnfubuki_map(address_map &map);
};

void hnayayoi_state::hnayayoi(machine_config &config)
{
    Z80(config, m_maincpu, 20000000/4);
    m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnayayoi_map);
    m_maincpu->set_addrmap(AS_IO, &hnayayoi_state::hnayayoi_io_map);
}

void hnayayoi_state::hnfubuki(machine_config &config)
{
    hnayayoi(config);

    m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnfubuki_map);
    m_maincpu->set_addrmap(AS_IO, address_map_constructor());
}

4. Accessing the spaces

address_space &space(int index = 0) const;

Returns the specified address space post-initialization. The specified address space must exist.

bool has_space(int index = 0) const;

Indicates whether a given space actually exists.

5. MMU support for disassembler

bool translate(int spacenum, int intention, offs_t &address, address_space *&target_space);

Does a logical to physical address translation through the device's MMU. spacenum gives the space number, intention for the type of the future access (TR_(READ\|WRITE\|FETCH)), address is an in/out parameter holding the address to translate on entry and the translated version on return, and finally target_space is the actual space the access would end up in, which may be in a different device. Should return true if the translation went correctly, or false if the address is unmapped. The call must not change the state of the device.

Note that for some historical reason, the device itself must override the virtual method memory_translate with the same signature.