In the simplest machine model, there is a single address space which is common to both the processor and programs. That is, the address that a program accesses in software is the same as the address that is output on the processor's address lines. In a more sophisticated model, there are two address spaces, namely physical and virtual. In this model, the address that a program uses is different than the address represented on the processor's address lines, as a result of a translation performed to make discontiguous physical memory appear as contiguous to software.
NT extends this concept further. In modern PC designs, there are multiple buses in the system. Typically, the CPU, external cache, and main memory reside on a high speed bus whose architecture is specific to the processor. The CPU bus is bridged to a standardized local bus, on which reside high speed devices such as the display. The local bus is bridged to secondary local buses and to external buses that host storage devices, network adapters, and communications ports. It is the multiple bus architecture of modern PCs that gives rise to a more complex address space model.
In order to access devices residing on buses other than the CPU bus, the system hardware must create a mapping between the CPU bus address and the corresponding address on the bus where the target device resides. Therefore, the address that the processor puts on its address lines and the address the target device is configured to decode may be different.
Some peripheral buses supply separate address spaces for I/O and memory operations. Similarly, some processors (such as Intel x86) recognize this distinction. The mapping that the system makes from a peripheral bus to the processor bus must take this into consideration. An address in the memory space of a peripheral bus always maps to a memory address on the CPU bus. However, an I/O address on the peripheral bus does not always map to a I/O address on the CPU bus. This is obviously the case if the CPU does not recognize an I/O space.
The system does not require the device driver writer to know the details of how each bus in the system is mapped to the processor's address space, but it does require taking into account that such a mapping may occur. For example, suppose it is learned from the registry that device D is on bus B of bus type I, and responds to I/O address A on that bus. In order to access that device, the driver must first request the system to map that device address information to a physical address on the CPU bus. The result could be in either the memory space or the I/O space on the CPU bus, depending on how the peripheral bus is hosted. If it is in memory space, a driver that performs programmed I/O to that device must make an additional request to the system in order to set up page tables to access that physical address.