# 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:

- Standard two-space vector: v60_device

- Conditional `AS_OPCODES`: z80_device

- Inherit configuration and add a space: hd647180x_device

- Inherit configuration and modify a space: tmpz84c011_device

    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.