I noticed that CPU's like the 8086 and especially the 8080 have the ability to access more memory than what one would normally assume. The 8080, for example, has an 8-bit word size but can use a 16-bit memory address. From what I understand, a standard instruction has the opcode in the beginning and then the address on which it will work. How then, could a 8-bit processor be able to fit an opcode and 16-bit address in 8-bits?
The 8080, as @TEMLIB pointed out, had 16 bit registers (the program counter, the stack pointer and BC, DE, and HL), and could access 216 bytes of memory. The 8080 had an 8-bit bus, and for instructions that required 2 bytes of data or address the bus would be used for 2 cycles.
The 8086 is more interesting. The 8086 had 16 bit registers, but could access 220 bytes of memory. It did this by using segment registers. There were four special 16-bit registers,
SS and every address was calculated by taking 16 times the appropriate segment register plus the 16-bit offset coming from a "normal" register. So for example, instruction addresses were all calculated as an offset from
CS * 16, and stack references were all calculated as an offset from
SS * 16.
Modern 32-bit processors can also sometimes access more than 232 bytes of DRAM through the virtual memory system. Each running program can access only 232 bytes of virtual memory but the page table entries map to (for example) 36 bit physical addresses, allowing the operating system to run multiple programs simultaneously that collectively can access up to 236 bytes of physical DRAM.
In a 8 bits CPU, all registers do not need to be 8bits wide. The program counter, or index registers, or register pairs, can be used as 16bits registers.
You should look at the 8080 instruction set, or another 8bits CPU, and get the difference between "inherent", "implicit", "indexed", "immediate" types of instructions.
Most CPU architectures use instructions of varying length. Even so-called RISC architectures with "fixed" instruction sizes support additional instruction operand words after the opcode word that determines the basic format of the instruction.
The x86 instruction set, famously, has instructions that vary wildly from 1 byte (8 bits) to as many as 15 bytes.
So, in the old 16-bit real mode x86 CPUs, an instruction to load from a fixed address in memory would normally be encoded in 3 bytes: one opcode byte to specify the instruction and the destination register and two additional operand bytes to specify the source memory address.
The size of addressable memory is not limited by the size of the memory data bus, the size of instructions, the size of arithmetic operands (e.g., register size), or even the size of addresses used by applications.
A processor can provide instructions larger than the data bus, allowing direct (absolute) addressing of a larger memory. For the 8080, immediate data was generally provided after the byte containing the opcode and not constrained to less than one byte (to allow space for the opcode) or even to one byte. This means that an instruction will take more than one memory cycle to fetch, but this can be an acceptable choice when memory is fast relative to processing and cost is a primary constraint. (This was more a historical consideration.)
(It is also possible to shift instruction constants to provide access to a greater range of aligned addresses.)
A processor can also choose not to provide direct addressing of the entire address space from instructions, allowing the largest instruction to be smaller than the size of addressable memory. Base plus offset addressing can provide access to an address space whose size is based on the size of the base address. The base address can be loaded from memory or using multiple instructions.
The size of the memory data bus also does not limit the size of base addresses (e.g., registers). The 8080 had a 16-bit Program Counter and a 16-bit Stack Pointer. The 8080 also allowed a register pair to be used to address memory. (Using register pairs for memory addressing is not uncommon for 8-bit processors.)
Providing larger special address registers is a somewhat common method to extend addressability. When these address registers do not have to be commonly saved and restored, their size can mostly be hidden from application software. The distinction between specialized segment registers and extended special address registers can be somewhat fuzzy.
Using segments is similar to using register pairs. Intel's 8086, HP's PA-RISC, and AIM's (Apple, IBM, Motorola) PowerPC provided segment registers that extended the addressability provided by registers. The 8086 implied the segment register by the base address register used with the offset in the segment register shifted by four and added to the base address. 32-bit PA-RISC could specify the segment in the instruction or by the two most significant bits in the address with the segment value being inserted as the most significant 32-bits (ORed in for the 64-bit ISA). 32-bit PowerPC used the most significant four bits of an effective address to specify a segment which was appended to the 28 bit inset.
(Bank switching is effectively a form of segmentation where the segment translation applies to all accesses, similar to PowerPC segments, but with typically very limited extension of addressability.)
Base registers (used to provide some degree of virtual addressing to allow multiple programs to see a full-sized address space) are a limited form of segment registers, whether applied to code only, code and data separately, or code and data with a common base offset.
A somewhat common ISA feature of indexed addressing also provides a mechanism to modestly extend addressability when the index is shifted by the size of the operand. This is more of a theoretical possibility.
Page-based address translation provides yet another method of allowing access to a larger physical address space. In page-based address translation, a virtual address is composed on a page number (potentially extend with an address space ID) and a page offset. For every valid page number, a base address (page frame number) is provided to which the page offset is added/inset. The size of this base address is independent of the size of registers. This is similar to PowerPC's use of segmentation to extend the effective address but using, e.g. in a 32-bit address with 4KiB pages, 20 bits of the effective address to specify the segment.
We should distinguish between what are termed the logical address and the physical address. The logical address is the address which an individual program uses. This depends on the instruction set, and is usually limited by such things as how big the address fields are in instructions and how many bits are in an index register. For example, all registers in a PDP-11 were 16 bits, and the biggest address field in an instruction was 16 bits, so any one program could ordinarily address up to 2^16 = 65536 = 64K bytes of memory.
Very quickly after the PDP-11 was introduced, it became apparent that users needed more physical memory. So memory-mapping hardware was designed. Under control of the operating system, the memory map would convert a 16-bit logical address into an 18-bit physical address (this was later expanded to 22 bits). So there could be many programs in memory at the same time, but each one could only address 64K. Part of the OS's job was keeping track of which blocks of physical memory were assigned to which blocks of logical memory for each program. Every time there was a process switch (i.e., when one program paused so another could run), the OS had to update the memory map.
Modern machines use virtual memory, but the memory mapping works essentially the same way.
Some 32-bit x86 processors use PAE (Physical Address Extension), which lets them address up to 2^36 bytes of physical memory, although each program is still limited to 2"32.
One of the designers of the PDP-11, Gordon Bell, devised Bell's Law, which basically says that you need an extra address bit every two years. That is, memory size doubles every two years. If you plot the maximum size and the average size of memory delivered in a PC, this has roughly held true for the past 40 years.
Just because a processor is "8-bit" doesn't mean all of its types and registers are 8-bit.
Case in point: the MOS 6502 processor. It has an 8-bit accumulator A, 8-bit index registers X, Y, and S, a 6-bit flags register P, a 16-bit program counter, and a 16-bit internal register to hold the operand address. Various addressing modes act on this operand address register.
LDA $2002loads 0x2002 into the operand address register, reads a byte from that address, and writes the value to register A.
STA $3FF0,Xloads 0x3FF0 into the operand address register, adds register X to that value (which may produce a carry), and writes the value in register A to that address.
- Indirect indexed:
LDA ($02),Yreads memory addresses 0x0002 and 0x0003 into the operand address register, adds register Y to that value (which may produce a carry), reads a byte from that address, and writes the value to register A. This is used for large arrays, as the program can add 1 to Y after reading each byte and then add 1 to the value at 0x0003 when Y wraps around to 0.
Because the 6502's ALU is still 8-bit, the CPU adds the least significant byte first and takes an extra cycle to perform a second 8-bit addition if there is a carry.
The CPU uses 16-bit addresses, which means it can see 216 = 65536 bytes (64 KiB) of memory at once. To go beyond this, several computers that use a 6502 processor implement bank switching, a memory controller feature that controls which parts of physical memory are visible to the CPU by changing values on the memory's upper address lines.
- The Apple IIe can switch a 12 KiB "language card" window (addresses 0xD000-0xFFFF) between ROM and RAM, and it can switch the 4 KiB subwindow (0xD000-0xDFFF) between two 4 KiB banks.
- The Family Computer (called Nintendo Entertainment System outside Japan) has bank switching circuits on the Game Pak's circuit board. These circuits, called "mappers", switch banks of ROM or RAM on the cartridge into windows 8 KiB to 32 KiB in size. There are well over a hundred different variants on NES mappers.
- The PC Engine (called TurboGrafx-16 outside Japan) has a HuC6280, a customized version of Rockwell's 65C02 with an integrated MMU that controls eight 8 KiB windows.