diff options
Diffstat (limited to 'docs/source/techspecs/memory.rst')
-rw-r--r-- | docs/source/techspecs/memory.rst | 964 |
1 files changed, 964 insertions, 0 deletions
diff --git a/docs/source/techspecs/memory.rst b/docs/source/techspecs/memory.rst new file mode 100644 index 00000000000..9f0a31758c7 --- /dev/null +++ b/docs/source/techspecs/memory.rst @@ -0,0 +1,964 @@ +Emulated system memory and address spaces management +==================================================== + +.. contents:: :local: + + +1. Overview +----------- + +The memory subsystem (emumem and addrmap) combines multiple functions +useful for system emulation: + +* address bus decoding and dispatching with caching +* static descriptions of an address map +* RAM allocation and registration for state saving +* interaction with memory regions to access ROM + +Devices create address spaces, e.g. decodable buses, through the +``device_memory_interface``. The machine configuration sets up address +maps to put in the address spaces, then the device can do read and +writes through the bus. + +2. Basic concepts +----------------- + +2.1 Address spaces +~~~~~~~~~~~~~~~~~~ + +An address space, implemented in the class **address_space**, +represents an addressable bus with potentially multiple sub-devices +connected requiring a decode. It has a number of data lines (8, 16, +32 or 64) called data width, a number of address lines (1 to 32) +called address width and an Endianness. In addition an address shift +allows for buses that have an atomic granularity different than a +byte. + +Address space objects provide a series of methods for read and write +access, and a second series of methods for dynamically changing the +decode. + + +2.2 Address maps +~~~~~~~~~~~~~~~~ + +An address map is a static description of the decode expected when +using a bus. It connects to memory, other devices and methods, and is +installed, usually at startup, in an address space. That description +is stored in an **address_map** structure which is filled +programmatically. + + +2.3 Shares, banks and regions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Memory shares are allocated memory zones that can be put in multiple +places in the same or different address spaces, and can also be +directly accessed from devices. + +Memory banks are zones that indirect memory access, giving the +possibility to dynamically and efficiently change where a zone +actually points to. + +Memory regions are read-only memory zones in which ROMs are loaded. + +All of these have names allowing to access them. + +2.4 Views +~~~~~~~~~ + +Views are a way to multiplex different submaps over a memory range +with fast switching. It is to be used when multiple devices map at +the same addresses and are switched in externally. They must be +created as an object of the device and then setup either statically in +a memory map or dynamically through ``install_*`` calls. + +Switchable submaps, aka variants, are named through an integer. An +internal indirection through a map ensures that any integer value can +be used. + + +3. Memory objects +----------------- + +3.1 Shares - memory_share +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + class memory_share { + const std::string &name() const; + void *ptr() const; + size_t bytes() const; + endianness_t endianness() const; + u8 bitwidth() const; + u8 bytewidth() const; + }; + +A memory share is a named allocated memory zone that is automatically +saved in save states and can be mapped in address spaces. It is the +standard container for memory that is shared between spaces, but also +shared between an emulated CPU and a driver. As such one has easy +access to its contents from the driver class. + +.. code-block:: C++ + + required_shared_ptr<uNN> m_share_ptr; + optional_shared_ptr<uNN> m_share_ptr; + required_shared_ptr_array<uNN, count> m_share_ptr_array; + optional_shared_ptr_array<uNN, count> m_share_ptr_array; + + [device constructor] m_share_ptr(*this, "name"), + [device constructor] m_share_ptr_array(*this, "name%u", 0U), + +At the device level, a pointer to the memory zone can easily be +retrieved by building one of these four finders. Note that like for +every finder calling ``target()`` on the finder gives you the base +pointer of the ``memory_share`` object. + +.. code-block:: C++ + + memory_share_creator<uNN> m_share; + + [device constructor] m_share(*this, "name", size, endianness), + +A memory share can be created if it doesn’t exist in a memory map +through that creator class. If it already exists it is just +retrieved. That class behaves like a pointer but also has the +``target()``, ``length()``, ``bytes()``, ``endianness()``, +``bitwidth()`` and ``bytewidth()`` methods for share information. The +desired size is specified in bytes. + +.. code-block:: C++ + + memory_share *memshare(string tag) const; + +The ``memshare`` device method retrieves a memory share by name. Beware +that the lookup can be expensive, prefer finders instead. + +3.2 Banks - memory_bank +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + class memory_bank { + const std::string &tag() const; + int entry() const; + void set_entry(int entrynum); + void configure_entry(int entrynum, void *base); + void configure_entries(int startentry, int numentry, void *base, offs_t stride); + void set_base(void *base); + void *base() const; + }; + +A memory bank is a named memory zone indirection that can be mapped in +address spaces. It points to ``nullptr`` when created. +``configure_entry`` associates an entry number and a base pointer. +``configure_entries`` does the same for multiple consecutive entries +spanning a memory zone. + +``set_base`` sets the base address for the active entry. If there are +no entries, entry 0 (zero) is automatically created and selected. Use +of ``set_base`` should be avoided in favour of pre-configured entries +unless there are an impractically large number of possible base +addresses. + +``set_entry`` dynamically and efficiently selects the active entry, +``entry()`` returns the active entry number, and ``base()`` gets the +associated base pointer. + +.. code-block:: C++ + + required_memory_bank m_bank; + optional_memory_bank m_bank; + required_memory_bank_array<count> m_bank_array; + optional_memory_bank_array<count> m_bank_array; + + [device constructor] m_bank(*this, "name"), + [device constructor] m_bank_array(*this, "name%u", 0U), + +At the device level, a pointer to the memory bank object can easily be +retrieved by building one of these four finders. + +.. code-block:: C++ + + memory_bank_creator m_bank; + + [device constructor] m_bank(*this, "name"), + +A memory bank can be created if it doesn’t exist in a memory map +through that creator class. If it already exists it is just +retrieved. + +.. code-block:: C++ + + memory_bank *membank(string tag) const; + +The ``membank`` device method retrieves a memory bank by name. Beware +that the lookup can be expensive, prefer finders instead. + + +3.3 Regions - memory_region +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + class memory_region { + u8 *base(); + u8 *end(); + u32 bytes() const; + const std::string &name() const; + endianness_t endianness() const; + u8 bitwidth() const; + u8 bytewidth() const; + u8 &as_u8(offs_t offset = 0); + u16 &as_u16(offs_t offset = 0); + u32 &as_u32(offs_t offset = 0); + u64 &as_u64(offs_t offset = 0); + } + +A region is used to store read-only data like ROMs or the result of +fixed decryptions. Their contents are not saved, which is why they +should not being written to from the emulated system. They don’t +really have an intrinsic width (``base()`` returns an ``u8 *`` always), +which is historical and pretty much unfixable at this point. The +``as_*`` methods allow for accessing them at a given width. + +.. code-block:: C++ + + required_memory_region m_region; + optional_memory_region m_region; + required_memory_region_array<count> m_region_array; + optional_memory_region_array<count> m_region_array; + + [device constructor] m_region(*this, "name"), + [device constructor] m_region_array(*this, "name%u", 0U), + +At the device level, a pointer to the memory region object can easily be +retrieved by building one of these four finders. + +.. code-block:: C++ + + memory_region *memregion(string tag) const; + +The ``memregion`` device method retrieves a memory region by name. +Beware that the lookup can be expensive, prefer finders instead. + + +3.4 Views - memory_view +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + class memory_view { + memory_view(device_t &device, std::string name); + memory_view_entry &operator[](int slot); + + void select(int entry); + void disable(); + + const std::string &name() const; + } + +A view allows to switch part of a memory map between multiple +possibilities, or even disable it entirely to see what was there +before. It is created as an object of the device. + +.. code-block:: C++ + + memory_view m_view; + + [device constructor] m_view(*this, "name"), + +It is then setup through the address map API or dynamically. At +runtime, a numbered variant can be selected using the ``select`` method, +or the view can be disabled using the ``disable`` method. A disabled +view can be re-enabled at any time. + + +.. _3.5: + +3.5 Bus contention handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some specific CPUs have been upgraded to be interruptible which allows +to add bus contention and wait states capabitilites. Being +interruptible means, in practice, that an instruction can be +interrupted at any time and the execute_run method of the core exited. +Other devices can then run, then eventually controls returns to the +core and the instruction continues from the point it was started. +Importantly, this can be triggered from a handler and even be used to +interrupt just before the access that is currently done +(e.g. continuation will redo the access). + +The CPUs supporting that declare their capability by overriding the +method ``cpu_is_interruptible`` to return true. + +Three intermediate contention handlers can be added to accesses: + +* ``before_delay``: wait a number of cycles before doing the access. +* ``after_delay``: wait a number of cycles after doing the access. +* ``before_time``: wait for a given time before doing the access. + +For the delay handlers, a method or lambda is called which returns the +number of cycles to wait (as a u32). + +The ``before_time`` is special. First, the time is compared to the +current value of cpu->total_cycles(). That value is the number of +cycles elapsed since the last reset of the cpu. It is passed as a +parameter to the method as a u64 and must return the earliest time as +a u64 when the access can be done, which can be equal to the passed-in +time. From there two things can happen: either the running cpu has +enough cycles left to consume to reach that time. In that case, the +necessary number of cycles is consumed, and the access is done. +Otherwise, when there isn't enough, the remaining cycles are consumed, +the access aborted, scheduling happens, and eventually the access is +redone. In that case the method is called again with the new current +time, and must return the (probably same) earliest time again. This +will happen until enough cycles to consume are available to directly +do the access. + +This approach allows to for instance handle consecutive DMAs. A first +DMA grabs the bus for a transfer. This shows up as the method +answering for the earliest time for access the time of the end of the +dma. If no timer happens until that time the access will then happen +just after the dma finishes. But if a timer elapses before that and +as a consequence another dma is queued while the first is running, the +cycle will be aborted for lack of remaining time, and the method will +eventually be called again. It will then give the time of when the +second dma will finish, and all will be well. + +It can also allow to reduce said earlier time when circumstances +require it. For instance a PIO latch that waits up to 64 cycles that +data arrives can indicate that current time + 64 as a target (which +will trigger a bus error for instance) but if a timer elapses and +fills the latch meanwhile the method will be called again and that +time can just return the current time to let the access pass though. +Beware that if the timer elapsing did not fill the latch then the +method must return the time it returned previously, e.g. the initial +access time + 64, otherwise irrelevant timers happening or simply +scheduling quantum effects will delay the timeout, possibly to +infinity if the quantum is small enough. + +Contention handlers on the same address are taken into account in the +``before_time``, ``before_delay`` then ``after_delay`` order. +Contention handlers of the same type on the same address at +last-one-wins. Installing any non-contention handler on a range where +a contention handler was removes it. + + +4. Address maps API +------------------- + +4.1 General API structure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +An address map is a method of a device which fills an **address_map** +structure, usually called **map**, passed by reference. The method +then can set some global configuration through specific methods and +then provide address range-oriented entries which indicate what should +happen when a specific range is accessed. + +The general syntax for entries uses method chaining: + +.. code-block:: C++ + + map(start, end).handler(...).handler_qualifier(...).range_qualifier().contention(); + +The values start and end define the range, the handler() block +determines how the access is handled, the handler_qualifier() block +specifies some aspects of the handler (memory sharing for instance) +and the range_qualifier() block refines the range (mirroring, masking, +lane selection, etc.). The contention methods handle bus contention +and wait states for cpus supporting them. + +The map follows a “last one wins” principle, where the handler specified +last is selected when multiple handlers match a given address. + + +4.2 Global configurations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +4.2.1 Global masking +'''''''''''''''''''' + +.. code-block:: C++ + + map.global_mask(offs_t mask); + +Specifies a mask to be applied to all addresses when accessing the space +that map is installed in. + + +4.2.2 Returned value on unmapped/nop-ed read +'''''''''''''''''''''''''''''''''''''''''''' + +.. code-block:: C++ + + map.unmap_value_low(); + map.unmap_value_high(); + map.unmap_value(u8 value); + +Sets the value to return on reads to an unmapped or nopped-out address. +Low means 0, high ~0. + + +4.3 Handler setting +~~~~~~~~~~~~~~~~~~~ + +4.3.1 Method on the current device +'''''''''''''''''''''''''''''''''' + +.. code-block:: C++ + + (...).r(FUNC(my_device::read_method)) + (...).w(FUNC(my_device::write_method)) + (...).rw(FUNC(my_device::read_method), FUNC(my_device::write_method)) + + uNN my_device::read_method(address_space &space, offs_t offset, uNN mem_mask) + uNN my_device::read_method(address_space &space, offs_t offset) + uNN my_device::read_method(address_space &space) + uNN my_device::read_method(offs_t offset, uNN mem_mask) + uNN my_device::read_method(offs_t offset) + uNN my_device::read_method() + + void my_device::write_method(address_space &space, offs_t offset, uNN data, uNN mem_mask) + void my_device::write_method(address_space &space, offs_t offset, uNN data) + void my_device::write_method(address_space &space, uNN data) + void my_device::write_method(offs_t offset, uNN data, uNN mem_mask) + void my_device::write_method(offs_t offset, uNN data) + void my_device::write_method(uNN data) + +Sets a method of the current device or driver to read, write or both +for the current entry. The prototype of the method can take multiple +forms making some elements optional. ``uNN`` represents ``u8``, +``u16``, ``u32`` or ``u64`` depending on the data width of the handler. +The handler can be narrower than the bus itself (for instance an 8-bit +device on a 32-bit bus). + +The offset passed in is built from the access address. It starts at +zero at the start of the range, and increments for each ``uNN`` unit. +An ``u8`` handler will get an offset in bytes, an ``u32`` one in double +words. The ``mem_mask`` has its bits set where the accessors actually +drive the bit. It’s usually built in byte units, but in some cases of +I/O chips ports with per-bit direction registers the resolution can be +at the bit level. + + +4.3.2 Method on a different device +'''''''''''''''''''''''''''''''''' + +.. code-block:: C++ + + (...).r(m_other_device, FUNC(other_device::read_method)) + (...).r("other-device-tag", FUNC(other_device::read_method)) + (...).w(m_other_device, FUNC(other_device::write_method)) + (...).w("other-device-tag", FUNC(other_device::write_method)) + (...).rw(m_other_device, FUNC(other_device::read_method), FUNC(other_device::write_method)) + (...).rw("other-device-tag", FUNC(other_device::read_method), FUNC(other_device::write_method)) + +Sets a method of another device, designated by an object finder +(usually ``required_device`` or ``optional_device``) or its tag, to +read, write or both for the current entry. + + +4.3.3 Lambda function +''''''''''''''''''''' + +.. code-block:: C++ + + (...).lr{8,16,32,64}(NAME([...](address_space &space, offs_t offset, uNN mem_mask) -> uNN { ... })) + (...).lr{8,16,32,64}([...](address_space &space, offs_t offset, uNN mem_mask) -> uNN { ... }, "name") + (...).lw{8,16,32,64}(NAME([...](address_space &space, offs_t offset, uNN data, uNN mem_mask) -> void { ... })) + (...).lw{8,16,32,64}([...](address_space &space, offs_t offset, uNN data, uNN mem_mask) -> void { ... }, "name") + (...).lrw{8,16,32,64}(NAME(read), NAME(write)) + (...).lrw{8,16,32,64}(read, "name_r", write, "name_w") + +Sets a lambda called on read, write or both. The lambda prototype can +be any of the six available for methods. One can either use ``NAME()`` +over the whole lambda, or provide a name after the lambda definition. +The number is the data width of the access, e.g. the NN. + + +4.3.4 Direct memory access +'''''''''''''''''''''''''' + +.. code-block:: C++ + + (...).rom() + (...).writeonly() + (...).ram() + +Selects the range to access a memory zone as read-only, write-only or +read/write respectively. Specific handler qualifiers specify the +location of this memory zone. There are two cases when no qualifier is +acceptable: + +* ``ram()`` gives an anonymous RAM zone not accessible outside of the + address space. + +* ``rom()`` when the memory map is used in an ``AS_PROGRAM`` + space of a (CPU) device which names is also the name of a region. + Then the memory zone points to that region at the offset + corresponding to the start of the zone. + +.. code-block:: C++ + + (...).rom().region("name", offset) + +The ``region`` qualifier causes a read-only zone point to the contents +of a given region at a given offset. + +.. code-block:: C++ + + (...).rom().share("name") + (...).writeonly.share("name") + (...).ram().share("name") + +The ``share`` qualifier causes the zone point to a shared memory region +identified by its name. If the share is present in multiple spaces, the +size, bus width, and, if the bus is more than byte-wide, the Endianness +must match. + + +4.3.5 Bank access +''''''''''''''''' + +.. code-block:: C++ + + (...).bankr("name") + (...).bankw("name") + (...).bankrw("name") + +Sets the range to point at the contents of a memory bank in read, write +or read/write mode. + + +4.3.6 Port access +''''''''''''''''' + +.. code-block:: C++ + + (...).portr("name") + (...).portw("name") + (...).portrw("name") + +Sets the range to point at an I/O port. + + +4.3.7 Dropped access +'''''''''''''''''''' + +.. code-block:: C++ + + (...).nopr() + (...).nopw() + (...).noprw() + +Sets the range to drop the access without logging. When reading, the +unmap value is returned. + + +4.3.8 Unmapped access +''''''''''''''''''''' + +.. code-block:: C++ + + (...).unmapr() + (...).unmapw() + (...).unmaprw() + +Sets the range to drop the access with logging. When reading, the +unmap value is returned. + + +4.3.9 Subdevice mapping +''''''''''''''''''''''' + +.. code-block:: C++ + + (...).m(m_other_device, FUNC(other_device::map_method)) + (...).m("other-device-tag", FUNC(other_device::map_method)) + +Includes a device-defined submap. The start of the range indicates +where the address zero of the submap ends up, and the end of the range +clips the submap if needed. Note that range qualifiers (defined +later) apply. + +Currently, only handlers are allowed in submaps and not memory zones +or banks. + + +4.4 Range qualifiers +~~~~~~~~~~~~~~~~~~~~ + +4.4.1 Mirroring +''''''''''''''' + +.. code-block:: C++ + + (...).mirror(mask) + +Duplicate the range on the addresses reachable by setting any of the 1 +bits present in mask. For instance, a range 0-0x1f with mirror 0x300 +will be present on 0-0x1f, 0x100-0x11f, 0x200-0x21f and 0x300-0x31f. +The addresses passed in to the handler stay in the 0-0x1f range, the +mirror bits are not seen by the handler. + + +4.4.2 Masking +''''''''''''' + +.. code-block:: C++ + + (...).mask(mask) + +Only valid with handlers, the address will be masked with the mask +before being passed to the handler. + + +4.4.3 Selection +''''''''''''''' + +.. code-block:: C++ + + (...).select(mask) + +Only valid with handlers, the range will be mirrored as with mirror, +but the mirror address bits are preserved in the offset passed to the +handler when it is called. This is useful for devices like sound +chips where the low bits of the address select a function and the high +bits a voice number. + + +4.4.4 Sub-unit selection +'''''''''''''''''''''''' + +.. code-block:: C++ + + (...).umask16(16-bits mask) + (...).umask32(32-bits mask) + (...).umask64(64-bits mask) + +Only valid with handlers and submaps, selects which data lines of the +bus are actually connected to the handler or the device. The mask value +should be a multiple of a byte, e.g. the mask is a series of 00 and ff. +The offset will be adjusted accordingly, so that a difference of 1 means +the next handled unit in the access. + +If the mask is narrower than the bus width, the mask is replicated in +the upper lines. + + +4.4.5 Chip select handling on sub-unit +'''''''''''''''''''''''''''''''''''''' + +.. code-block:: C++ + + (...).cselect(16/32/64) + +When a device is connected to part of the bus, like a byte on a +16-bits bus, the target handler is only activated when that part is +actually accessed. In some cases, very often byte access on a 68000 +16-bits bus, the actual hardware only checks the word address and not +if the correct byte is accessed. ``cswidth`` tells the memory system to +trigger the handler if a wider part of the bus is accessed. The +parameter is that trigger width (would be 16 in the 68000 case). + + +4.4.6 User flags +'''''''''''''''' + +.. code-block:: C++ + + (...).flags(16-bits mask) + +This parameter allows to set user-defined flags on the handler which +can then be retrieved by an accessing device to change their +behaviour. An example of use the i960 which marks burstable zones +that way (they have a specific hardware-level support). + + +4.5 Contention +~~~~~~~~~~~~~~ + +.. code-block:: C++ + + (...).before_time(method).(...) + (...).before_delay(method).(...) + (...).after_delay(method).(...) + +These three methods allow to add the contention methods to a handler. +See section `3.5`_. Multiple methods can be handler to one handler. + + +4.6 View setup +~~~~~~~~~~~~~~ + +.. code-block:: C++ + + map(start, end).view(m_view); + m_view[0](start1, end1).[...]; + +A view is setup in a address map with the view method. The only +qualifier accepted is mirror. The “disabled” version of the view will +include what was in the range prior to the view setup. + +The different variants are setup by indexing the view with the variant +number and setting up an entry in the usual way. The entries within a +variant must of course stay within the range. There are no other +additional constraints. The contents of a variant, by default, are +what was there before, i.e. the contents of the disabled view, and +setting it up allows part or all of it to be overridden. + +Variants can only be setup once the view itself has been setup with +the ``view`` method. + +A view can only be put in one address map and in only one position. +If multiple views have identical or similar contents, remember that +setting up a map is nothing more than a method call, and creating a +second method to setup a view is perfectly reasonable. A view is of +type ``memory_view`` and an indexed entry (e.g. a variant to setup) is +of type ``memory_view::memory_view_entry &``. + +A view can be installed in another view, but don’t forget that a view +can be installed only once. A view can also be part of “what was there +before”. + + + +5. Address space dynamic mapping API +------------------------------------ + +5.1 General API structure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A series of methods allow the bus decoding of an address space to be +changed on-the-fly. They’re powerful but have some issues: + +* changing the mappings repeatedly can be slow +* the address space state is not saved in the saved states, so it has to + be rebuilt after state load +* they can be hidden anywhere rather than be grouped in an address map, + which can be less readable + +The methods, rather than decomposing the information in handler, handler +qualifier and range qualifier, put them all together as method +parameters. To make things a little more readable, lots of them are +optional. + + +5.2 Handler mapping +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + uNN my_device::read_method(address_space &space, offs_t offset, uNN mem_mask) + uNN my_device::read_method_m(address_space &space, offs_t offset) + uNN my_device::read_method_mo(address_space &space) + uNN my_device::read_method_s(offs_t offset, uNN mem_mask) + uNN my_device::read_method_sm(offs_t offset) + uNN my_device::read_method_smo() + + void my_device::write_method(address_space &space, offs_t offset, uNN data, uNN mem_mask) + void my_device::write_method_m(address_space &space, offs_t offset, uNN data) + void my_device::write_method_mo(address_space &space, uNN data) + void my_device::write_method_s(offs_t offset, uNN data, uNN mem_mask) + void my_device::write_method_sm(offs_t offset, uNN data) + void my_device::write_method_smo(uNN data) + + readNN_delegate (device, FUNC(read_method)) + readNNm_delegate (device, FUNC(read_method_m)) + readNNmo_delegate (device, FUNC(read_method_mo)) + readNNs_delegate (device, FUNC(read_method_s)) + readNNsm_delegate (device, FUNC(read_method_sm)) + readNNsmo_delegate(device, FUNC(read_method_smo)) + + writeNN_delegate (device, FUNC(write_method)) + writeNNm_delegate (device, FUNC(write_method_m)) + writeNNmo_delegate (device, FUNC(write_method_mo)) + writeNNs_delegate (device, FUNC(write_method_s)) + writeNNsm_delegate (device, FUNC(write_method_sm)) + writeNNsmo_delegate(device, FUNC(write_method_smo)) + +To be added to a map, a method call and the device it is called onto +have to be wrapped in the appropriate delegate type. There are twelve +types, for read and for write and for all six possible prototypes. +Note that as all delegates, they can also wrap lambdas. + +.. code-block:: C++ + + space.install_read_handler(addrstart, addrend, read_delegate, unitmask, cswidth, flags) + space.install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, unitmask, cswidth, flags) + space.install_write_handler(addrstart, addrend, write_delegate, unitmask, cswidth, flags) + space.install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, write_delegate, unitmask, cswidth, flags) + space.install_readwrite_handler(addrstart, addrend, read_delegate, write_delegate, unitmask, cswidth, flags) + space.install_readwrite_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, write_delegate, unitmask, cswidth, flags) + +These six methods allow to install delegate-wrapped handlers in a live +address space. Either plain or with mask, mirror and select. In the +read/write case both delegates must be of the same flavor (``smo`` +stuff) to avoid a combinatorial explosion of method types. The +``unitmask``, ``cswidth`` and ``flags`` arguments are optional. + +5.3 Direct memory range mapping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.install_rom(addrstart, addrend, void *pointer) + space.install_rom(addrstart, addrend, addrmirror, void *pointer) + space.install_rom(addrstart, addrend, addrmirror, flags, void *pointer) + space.install_writeonly(addrstart, addrend, void *pointer) + space.install_writeonly(addrstart, addrend, addrmirror, void *pointer) + space.install_writeonly(addrstart, addrend, addrmirror, flags, void *pointer) + space.install_ram(addrstart, addrend, void *pointer) + space.install_ram(addrstart, addrend, addrmirror, void *pointer) + space.install_ram(addrstart, addrend, addrmirror, flags, void *pointer) + +Installs a memory block in an address space, with or without mirror +and flags. ``_rom`` is read-only, ``_ram`` is read/write, +``_writeonly`` is write-only. The pointer must be non-null, this +method will not allocate the memory. + +5.4 Bank mapping +~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.install_read_bank(addrstart, addrend, memory_bank *bank) + space.install_read_bank(addrstart, addrend, addrmirror, memory_bank *bank) + space.install_read_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank) + space.install_write_bank(addrstart, addrend, memory_bank *bank) + space.install_write_bank(addrstart, addrend, addrmirror, memory_bank *bank) + space.install_write_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank) + space.install_readwrite_bank(addrstart, addrend, memory_bank *bank) + space.install_readwrite_bank(addrstart, addrend, addrmirror, memory_bank *bank) + space.install_readwrite_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank) + +Install an existing memory bank for reading, writing or both in an +address space. + +5.5 Port mapping +~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.install_read_port(addrstart, addrend, const char *rtag) + space.install_read_port(addrstart, addrend, addrmirror, const char *rtag) + space.install_read_port(addrstart, addrend, addrmirror, flags, const char *rtag) + space.install_write_port(addrstart, addrend, const char *wtag) + space.install_write_port(addrstart, addrend, addrmirror, const char *wtag) + space.install_write_port(addrstart, addrend, addrmirror, flags, const char *wtag) + space.install_readwrite_port(addrstart, addrend, const char *rtag, const char *wtag) + space.install_readwrite_port(addrstart, addrend, addrmirror, const char *rtag, const char *wtag) + space.install_readwrite_port(addrstart, addrend, addrmirror, flags, const char *rtag, const char *wtag) + +Install ports by name for reading, writing or both. + +5.6 Dropped accesses +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.nop_read(addrstart, addrend, addrmirror, flags) + space.nop_write(addrstart, addrend, addrmirror, flags) + space.nop_readwrite(addrstart, addrend, addrmirror, flags) + +Drops the accesses for a given range with an optional mirror and flags; + +5.7 Unmapped accesses +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.unmap_read(addrstart, addrend, addrmirror, flags) + space.unmap_write(addrstart, addrend, addrmirror, flags) + space.unmap_readwrite(addrstart, addrend, addrmirror, flags) + +Unmaps the accesses (e.g. logs the access as unmapped) for a given range +with an optional mirror and flags. + +5.8 Device map installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.install_device(addrstart, addrend, device, map, unitmask, cswidth, flags) + +Install a device address with an address map in a space. The +``unitmask``, ``cswidth`` and ``flags`` arguments are optional. + +5.9 Contention +~~~~~~~~~~~~~~ + +.. code-block:: C++ + + using ws_time_delegate = device_delegate<u64 (offs_t, u64)>; + using ws_delay_delegate = device_delegate<u32 (offs_t)>; + + space.install_read_before_time(addrstart, addrend, addrmirror, ws_time_delegate) + space.install_write_before_time(addrstart, addrend, addrmirror, ws_time_delegate) + space.install_readwrite_before_time(addrstart, addrend, addrmirror, ws_time_delegate) + + space.install_read_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + space.install_write_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + space.install_readwrite_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + + space.install_read_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + space.install_write_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + space.install_readwrite_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate) + +Install a contention handler in the decode path. The addrmirror +parameter is optional. + + +5.10 View installation +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: C++ + + space.install_view(addrstart, addrend, view) + space.install_view(addrstart, addrend, addrmirror, view) + + view[0].install... + +Installs a view in a space. This can be only done once and in only +one space, and the view must not have been setup through the address +map API before. Once the view is installed, variants can be selected +by indexing to call a dynamic mapping method on it. + +A view can be installed into a variant of another view without issues, +with only the usual constraint of single installation. + +5.11 Taps +~~~~~~~~~ + +.. code-block:: C++ + + using tap = std::function<void (offs_t offset, uNN &data, uNN mem_mask) + + memory_passthrough_handler mph = space.install_read_tap(addrstart, addrend, name, read_tap, &mph); + memory_passthrough_handler mph = space.install_write_tap(addrstart, addrend, name, write_tap, &mph); + memory_passthrough_handler mph = space.install_readwrite_tap(addrstart, addrend, name, read_tap, write_tap, &mph); + + mph.remove(); + +A tap is a method that is be called when a specific range of addresses +is accessed without overriding the actual access. Taps can change the +data passed around. A write tap happens before the access, and can +change the value to be written. A read tap happens after the access, +and can change the value returned. + +Taps must be of the same width and alignement than the bus. Multiple +taps can act over the same addresses. + +The ``memory_passthrough_handler`` object collates a number of taps +and allow to remove them all in one call. The ``mph`` parameter is +optional and a new one will be created if absent. + +Taps are lost when a new handler is installed at the same addresses +(under the usual principle of last one wins). If they need to be +preserved, one should install a change notifier on the address space, +and remove + reinstall the taps when notified. + |