Memory addressing refers to the ways in which a microcontroller accesses data to carry out an instruction. The instruction contains operands that specify the data to be operated on, while the program counter provides the address of the next instruction in program memory. Memory addressing modes are determined when a microcontroller is designed and cannot be changed by a programmer.
In general, memory can be addressed directly or indirectly. Direct addressing occurs when the memory address is stored as part of the instruction itself. Direct addressing is typically fast (the data to be operated on in memory is pointed to directly), and has the flexibility of allowing variable data to be manipulated, rather than constant data (which is used in immediate addressing).
Indirect addressing occurs when a pointer is used instead of the memory address. Indirect addressing enables a microcontroller to access memory contents even if the number of addresses available exceeds the size of the instruction itself, but suffers the drawback of being slower than direct addressing (first the pointer must be loaded, then the contents of the memory address that the pointer points to is operated on).
General purpose register addressing occurs when one or more operand is a general purpose register. The two types of general purpose register addressing modes in the AVR instruction set are
Direct, single register addressing instructions have only a single general purpose register operand. This operand is known as a destination register and is denoted as Rd. The destination register is capable of addressing the entire general purpose register space, which means that 5ย bits are used in the instruction to represent this operand. Direct, single register addressing is shown schematically in Figureย 13.4.1.
The twoโs complement instruction (NEG) uses direct, single register addressing. This instruction takes the twoโs complement value of a register and saves the result back into that same register. It is capable of addressing all of the 32 general purpose registers using 5 bits in the instruction. The remaining 11 bits are used for the opcode. The 16-bit machine instruction is 1001010ddddd0001, where the bits denoted d represent the value of the general purpose register.
Direct, two register addressing instructions have two general purpose register operands. These operands consist of a destination register (Rd) and a source register (Rr). Both operands can address the entire general purpose register space, which means that 10 bits are used in the instruction to represent the operands. Direct, two register addressing is shown schematically in Figureย 13.4.3.
The add without carry instruction (ADD) uses direct, two register addressing. This instruction takes the sum of two values (each of which is stored in a general purpose register) and saves the result back into the destination register. The instruction is capable of addressing all of the 32 general purpose registers using 10 bits (5 each for the source and destination operands) in the instruction. The remaining 6 bits are used for the opcode. The 16-bit machine instruction is 000011rdddddrrrr, where the bits denoted d represent the value of the destination register and the bits denoted r represent the value of the source register.
In immediate addressing, the instruction contains data to be operated on immediately. In other words, one or more operand is part of the instruction itself. Because the data is part of the instruction, immediate addressing can be fast. However, it is also less flexible when it comes to changing or repurposing code.
Most of the instructions that contain immediate addressing in the ATmega328P allow for the immediate data to take on values between 0-255. This requires 8ย bits of data, which means that only 8 bits remain in the instruction for the opcode and any other operand. AVR instructions that use immediate addressing use a general purpose register as the other operand. The number of general purpose registers that can be addressed is limited to registers 16-31, which means that 4 bits are used to address the register. The remaining 4 bits of the instruction are used to store the opcode.
Not all arithmetic and logical operations have immediate addressing instructions. In that case, an immediate can be loaded into a general purpose register using the load immediate (LDI) instruction. Then, the data stored in those registers can be operated on using general purpose register addressing.
The logical AND instruction (AND) and logical AND with immediate instruction (ANDI) demonstrate the difference between two-register addressing and immediate addressing. AND is a logical AND operation that occurs between two general purpose registers (Rd and Rr). The data is fetched from each of the registers, routed to the ALU, and then stored in the destination register. All of the general purpose registers can be addressed in this instruction, leaving 6 bits for the opcode. The machine instruction is for AND is 001000rdddddrrrr.
ANDI is the immediate addressing instruction used to compute a logical AND. With ANDI, the microcontroller performs a logical AND between the contents of general purpose register Rd and a constant (the immediate), with the result stored in the destination register. 8 bits of the instruction are used for the immediate, 4 bits are used for the general purpose register, leaving 4 bits for the opcode. The machine instruction for ANDI is 0111KKKKddddKKKK, where the bits denoted d represent the value of the destination register and the bits denoted K represent the value of the immediate.
I/O register addressing instructions have two operands: one thatโs a general purpose register and the other thatโs an I/O register. The general purpose register operand can address the entire general purpose register space (which requires 5 bits of the machine instruction). Because the I/O register space is 64ย bits, 6 bits of instruction is required to represent the I/O register operand. I/O register addressing is shown schematically in Figureย 13.4.6.
In I/O direct addressing, instructions contain an I/O address (A) as well as a destination general purpose register (Rd) and/or a source general purpose register (Rr).
Notable instructions that use direct I/O addressing are IN and OUT, which loads data from an I/O register into a destination general purpose register or stores the contents from a source register into one of the I/O registers, respectively. 6 bits are used to address the I/O register, 5 bits are used to address the (source or destination) general purpose register, and the remaining 5 bits are used for the opcode.
Data memory addressing is used to access the entirety of data memory (SRAM). These instructions contain two 16-bit words; the least significant word contains the 16-bit data memory address. Because there are two instruction words to decode, these instructions require at least 2 clock cycles to execute.
Data direct addressing has two operands: a general purpose register and an address in data memory. Data direct addressing is shown schematically in Figureย 13.4.7. The general purpose operand is capable of addressing the entire data memory space, and therefore requires 5 bits of the instruction. The data memory address is 8ย bits and can therefore address 65k of memory (this is more than sufficient to address the 2k data memory in the ATmega328P).
The load direct from data space instruction (LDS) loads data from an address in SRAM into a destination general purpose register. 16 bits are available for the data memory address, which means that it is limited to addressing the first 64k of data stored in SRAM. This is not a problem on the ATmega328P which only has 2k of memory. The remaining part of the instruction contains 5 bits to address the general purpose register, with the remaining 11 bits used for the opcode. The 32-bit machine instruction is 1001000ddddd0000 kkkkkkkkkkkkkkkk, where the bits denoted d represent the value of the destination register and the bits denoted k represent the data memory address.
Data indirect addressing instructions have two operands: a general purpose register and a pointer register (X, Y, or Z). This type of addressing can be used when the address of a desired memory location is not known at the time that a program is written. Data indirect addressing is shown schematically in Figureย 13.4.9, is used to access extended I/O registers or the internal SRAM.
Data indirect with displacement addressing, shown in Figureย 13.4.10 is used when the operand address is the result of the contents of Y or Z pointer registers added to the address contained in 6 bits (q) of the instruction word. This mode is useful for writing position-independent code, since an address register can be used to stored a base address, with data access using displacements relative to the base.
Figure13.4.10.Data indirect addressing with displacement occurs when an operand is a pointer register whose contents are added to 6 bits (q) contained in the instruction word to point to an SRAM address.
Data indirect with pre-decrement addressing is used when the operand address is the contents of the X, Y or Z register, which is automatically decremented before the operation. This addressing mode is useful for accessing array contents, for example. It is depicted schematically in Figureย 13.4.11.
Figure13.4.11.Data indirect with pre-decrement addressing occurs when an operand is a pointer register whose contents are decremented prior to the instruction and then used to point to an SRAM address.
Data indirect with post-increment addressing is used when the operand address is the contents of the X, Y or Z register, which is automatically incremented after the operation. This addressing mode is also useful for accessing array contents. It is depicted schematically in Figureย 13.4.12.
Figure13.4.12.Data indirect with post-increment addressing occurs when an operand is a pointer register whose contents point to an SRAM address and then is incremented after the operation.
Program memory addressing is used to access the full program memory space. It requires at least 14 bits to address the total flash memory section on the ATmega328P. The Z pointer register is used to refer to the address in program memory. Because program memory stores 16-bit words, and the destination register can only store 8 bits of data, the Z register additionally specifies if the LOW byte or HIGH byte should be returned. Program memory addressing can also be done with a post-increment or with a pre-decrement. Program memory addressing is shown schematically in Figureย 13.4.13.
Figure13.4.13.Program memory addressing accesses the full memory space given by the address in pointer register Z. The LSB of Z indicates if the low or high byte should be returned as the result.
In direct program addressing, instructions can contain a pointer (indirect) to be loaded to the program counter, or a 16-bit word (immediate) following the instruction to load to the program counter. The program counter can also be incremented by a constant k. Program execution will then continue at that space in program memory. This is depicted in Figureย 13.4.14.
The types of instructions that use direct programming addressing include the jump instruction (JMP), which causes the program counter to load an immediate value and continue executing instructions from that location in memory and the extended indirect jump instruction (EIJUMP), which has the program counter jump to the memory location stored in pointer register Z.
Bit addressing instructions contain one opcode that specifies one bit to be modified in a register. Because data registers are 8 bits wide, each bit opcode requires 3 bits (b) to specify which bit (between 0-7) is to be read from/written to. Bit addressing is depicted in Figureย 13.4.15.
The clear bit in I/O register instruction (CBI) clears a single bit in an I/O register. The instruction can only address the lower 32 I/O registers because only 5 bits are used in the instruction to define the data memory address. The 16-bit machine instruction is 10011000AAAAAbbb, where the bits denoted A represent the I/O register and the bits denoted b represent the specific bit to be cleared in the corresponding I/O register.