Bank switching
Bank switching is a memory management technique employed in computer architectures, particularly in 8-bit microcontrollers and early personal computers, to expand the effective size of code and data memory beyond the processor's native addressable limit without requiring an increase in the width of the address bus.[1] This method partitions the total memory into multiple discrete banks, each typically of equal size (e.g., 64 KB in systems like the Motorola 68HC11), with only one bank active and accessible to the CPU at any given time.[1] The active bank is selected through dedicated hardware mechanisms, such as bank selection instructions or control registers that concatenate a bank identifier with the processor's virtual address to form the physical address, effectively treating bank switching as a simplified form of memory mapping without full page descriptors.[1] Originating in the mid-1960s with early minicomputers, alongside more advanced memory mapping techniques such as virtual memory, bank switching became particularly prevalent in the 1970s and 1980s for resource-constrained embedded systems and personal computers limited to 16-bit address spaces (e.g., 64 KB total). In practice, it enabled memory expansion in devices like the Zilog Z80, Intel 8051, and PIC16F877A microcontrollers, where switching between banks (e.g., four 128-byte data banks in the PIC16F877A) is triggered by explicit instructions, incurring overhead in code size and execution cycles due to the need to manage bank selections before accessing variables or code in non-active banks.[1] For instance, in S-100 bus systems popular in the late 1970s, bank switching allowed expansion to 1 MB or more by selecting among multiple 64 KB banks via DIP switches, I/O ports, or software controls, supporting applications in early personal computing like the Apple II and TRS-80.[2] The primary advantages of bank switching include reduced hardware complexity and cost, as it avoids the need for wider address buses or more complex mapping hardware, while also potentially lowering power consumption and increasing clock speeds in embedded designs.[1] However, it introduces challenges such as bank conflicts—where frequent switches serialize operations and degrade performance—and limitations on program size, as no single program can span multiple banks without explicit management, making data exchange between banks cumbersome.[1] Compiler optimizations, such as those using partitioned Boolean quadratic programming to minimize selection instructions, have been developed to mitigate these issues, yielding reductions in memory usage (2.7%–18.2%) and cycles (5.1%–28.8%) in benchmarks for microcontrollers like the PIC16F877A.[1] Although largely superseded in modern 32- and 64-bit architectures by virtual memory and larger address spaces, bank switching remains relevant in low-cost, memory-constrained IoT and embedded systems, for example, in RISC-V processors and low-end microcontrollers as of 2024.[3]Fundamentals
Definition and Purpose
Bank switching is a hardware-based memory management technique that enables a computer system to access more memory than its processor's native address bus can directly support. It achieves this by partitioning the total physical memory into fixed-size units known as "banks," typically matching the size of the processor's address space, such as 64 KB segments for 16-bit addressing systems. These banks are selectively mapped into the processor's visible address space through hardware controls, allowing only one or a subset to be active at a time while others remain dormant.[4] The primary purpose of bank switching is to circumvent the inherent limitations of processors with constrained address buses, such as those limited to 64 KB of directly addressable memory, thereby permitting the installation and utilization of larger total memory capacities without necessitating a complete redesign of the processor architecture. This approach was particularly valuable in resource-constrained environments where expanding the address bus width would increase hardware complexity and cost. Key benefits include its relative simplicity in implementation, relying on basic hardware elements like latches or registers for bank selection, and minimal runtime overhead, making it ideal for static memory applications such as program code storage in read-only memory (ROM).[4][5] Bank switching emerged in the 1960s amid the rapid decline in memory costs, which outpaced advancements in processor addressing capabilities, creating a demand for techniques to scale memory economically in early minicomputer systems. For instance, by the 1970s and 1980s, 8-bit processors, commonly limited to 64 KB addressing, increasingly required bank switching to accommodate ROM or EPROM cartridges exceeding this limit in consumer devices like video game consoles.[4][5][6] Unlike virtual memory, which involves operating system-mediated abstraction, protection, and swapping between RAM and secondary storage like disks, bank switching operates at the hardware level without OS intervention or disk involvement, focusing solely on expanding direct physical memory access through explicit program-controlled switching.[4]Basic Mechanism
Bank switching enables a processor with a limited address space to access larger amounts of physical memory by selectively mapping different memory banks into the visible address range. The core process involves the processor writing a bank identifier to a dedicated bank select register, often via an I/O port instruction or memory-mapped I/O. This write operation latches the bank number into the register, after which a hardware decoder interprets the value to assert chip select signals for the target memory bank while deasserting them for others, thereby routing the processor's address and data signals exclusively to the active bank.[4][7] At the hardware level, the mechanism relies on address decoding logic to interpret the processor's address bus, combined with latches—such as the 74LS373 octal D-type transparent latch—to stably hold the bank select value until the next switch. Multiplexers then remap the high-order address bits by substituting bits from the bank register in place of unused upper address lines from the processor, effectively extending the addressable space. For instance, in a basic circuit for switching between two 64 KB banks using a single control bit, the processor's 16-bit address bus connects directly to the lower 16 bits of each bank's address inputs, while the latched select bit drives a decoder (e.g., a 74LS138 3-to-8 line decoder configured for binary selection) to enable one bank's chip select and a multiplexer (e.g., 74LS157 quad 2-to-1) to prepend the select bit as the 17th address line for the physical memory array.[7][8][9] Software plays an active role by explicitly managing bank switches through dedicated routines, typically implemented as inline assembly or function calls that perform port writes or interrupt-driven handlers to load content from inactive banks into the active space; unlike virtual memory systems, no hardware-managed automatic translation occurs, requiring programmers to track bank states manually.[4][8] Address space mapping in bank switching generally affects only the upper bits of the address bus, leaving lower bits unchanged for direct access within the bank. For example, a 16-bit processor address bus paired with a 2-bit bank select register supports 256 KB of total memory organized into four 64 KB banks, where the two highest bits are replaced by the latched bank value during decoding. Bank sizes remain fixed per system design, often 16 KB or 64 KB, to align with common dynamic RAM (DRAM) chip capacities and simplify decoding logic.[4][7] A representative software routine for initiating a bank switch might appear in pseudocode as follows:This simple operation, such asprocedure SwitchBank(bank_id: integer); begin OUT(0xFF, bank_id); // Write the bank number to I/O port 0xFF, latching it into the select register end;procedure SwitchBank(bank_id: integer); begin OUT(0xFF, bank_id); // Write the bank number to I/O port 0xFF, latching it into the select register end;
SwitchBank(3); to activate bank 3, immediately remaps the address space without further overhead, allowing seamless continuation of program execution in the new bank.[7][8]
In contrast to software overlays, which manage memory by dynamically loading and unloading program modules from secondary storage under explicit program control to fit within physical limits, bank switching provides hardware-mediated swapping of pre-loaded RAM blocks, avoiding disk access delays and supporting rapid context shifts for resident code and data.[10][4]