CODE EXPLINATIONS
; Replace with your application code
.INCLUDE "M32DEF.INC"
.ORG 0x00
LDI R20, 0x25
LDI R21, 0xF2
ADD R20, R21
📌 Line-by-Line Explanation
1. ; Replace with your application code
o This is a comment, ignored by the assembler.
o Used by the programmer to describe or annotate the code.
o Good coding tradition: Always comment your code!
2. .INCLUDE "M32DEF.INC"
o This tells the assembler to include the definition file for ATmega32 microcontroller.
o It contains all the register names, bit definitions, I/O ports, etc., so you don’t need to
remember raw addresses.
o Like including a header file in C (#include <avr/io.h>).
3. .ORG 0x00
o Sets the origin of the program — the starting address in memory.
o All AVR programs start at address 0x00 after reset, so this ensures the code runs from the
beginning.
4. LDI R20, 0x25
o Load Immediate: Loads the value 0x25 (decimal 37) into register R20.
o So now R20 = 0x25.
5. LDI R21, 0xF2
o Loads the immediate value 0xF2 (decimal 242) into register R21.
o Now R21 = 0xF2.
6. ADD R20, R21
o Adds the value in R21 to R20 and stores the result in R20.
o So now R20 = 0x25 + 0xF2 = 0x117 (decimal 279).
o Since registers only hold 8-bit data (0–255), this result overflows.
Final value in R20 = 0x17 (lower 8 bits of 0x117).
Carry flag in status register (SREG) will be set due to overflow.
Line-by-Line Explanation
.DEF NUM=R20
.DEF DENOMINATOR=R21
.DEF QUOTIENT=R22
– These lines define aliases for general-purpose registers.
– Instead of remembering which register is what, you can use names like NUM, DENOMINATOR, and
QUOTIENT.
LDI NUM,0x64
– Loads the numerator (value to be divided) 0x64 (decimal 100) into register R20.
LDI DENOMINATOR,0x04
– Loads the denominator (divisor) 0x04 (decimal 4) into R21.
CLR QUOTIENT
– Clears the quotient register (R22). It sets QUOTIENT = 0.
L1: INC QUOTIENT
– Label L1 is the beginning of a loop.
– Increments the quotient by 1 every time the loop runs.
SUB NUM, DENOMINATOR
– Subtracts the denominator from the numerator.
– Effectively: NUM = NUM - DENOMINATOR
BRCC L1
– Branch if Carry is Clear: Continue looping if subtraction didn’t cause a borrow.
– If NUM is still ≥ 0, go back to label L1.
DEC QUOTIENT
– Decrements the quotient once.
– Why? Because the loop counts one extra before the BRCC detects borrow, so this corrects the final result.
ADD NUM, DENOMINATOR
– Adds the denominator back to NUM to restore the last value before the borrow.
– Result: NUM now contains the remainder.
HERE: JMP HERE
– An infinite loop to stop the program.
– A traditional way of ending microcontroller code.
Line-by-Line Explanation
LDI R16, 0x70
– Load the value 0x70 (binary: 0111 0000) into register R16.
ANDI R16, 0x68
– Perform a bitwise AND between the contents of R16 and 0x68 (binary: 0110 1000).
AND Operation:
R16 = 0111 0000
0x68 = 0110 1000
Result= 0110 0000 => 0x60
So now, R16 = 0x60.
LDI R16, 0x0F
– This line overwrites R16 with a new value 0x0F (binary: 0000 1111), discarding the result from the ANDI.
LDI R19, $28
– Loads immediate value 0x28 (decimal 40) into register R19.
STS $68, R19
– STS = Store Direct to SRAM.
– Stores the value in R19 into SRAM address 0x68.
LDS R19, $68
– LDS = Load Direct from SRAM.
– Loads back the value from address 0x68 into register R19 (confirms data was correctly written).
OUT PORTD, R19
– Outputs the contents of R19 (which is 0x28) to the PORTD I/O register.
– You will see this value reflected on the output pins of PORTD (e.g., turning on specific LEDs).
📘 Part 1: Stack Initialization
.INCLUDE "M32DEF.INC"
.ORG 0x0
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
INCLUDE "M32DEF.INC": Brings in definitions for registers like PORTB, SPH, etc. from the
ATmega32 library.
LDI R16, HIGH(RAMEND): Loads the high byte of the RAM end address into R16.
OUT SPH, R16: Sets the stack pointer high (SPH) register.
LDI R16, LOW(RAMEND): Loads the low byte of RAM end address.
OUT SPL, R16: Sets the stack pointer low (SPL) register.
🧠 Why? Stack pointer must be initialized before using subroutines (like CALL DELAY) since return
addresses are stored on the stack.
📘 Part 2: Main Loop
BACK:
LDI R16, 0x33
OUT PORTB, R16
CALL DELAY
LDI R16, 0xCC
OUT PORTB, R16
CALL DELAY
RJMP BACK
LDI R16, 0x33: Load hex 33 (binary 00110011) into R16.
OUT PORTB, R16: Send that value to PORTB (turn on certain LEDs).
CALL DELAY: Calls the delay subroutine to pause.
Then we load 0xCC (binary 11001100) and send that to PORTB.
RJMP BACK: Unconditional jump back to label BACK, forming an infinite loop.
🧠 This makes PORTB alternate between 0x33 and 0xCC forever with delay — looks like blinking LEDs in
a pattern.
📘 Part 3: Delay Subroutine
.ORG 0x300
DELAY:
LDI R20, 0x7F
AGAIN:
NOP
NOP
DEC R20
BRNE AGAIN
RET
.ORG 0x300: Places the delay subroutine at program memory address 0x300.
LDI R20, 0x7F: Load 127 into register R20.
NOP: No operation (used for wasting clock cycles).
DEC R20: Decrement R20.
BRNE AGAIN: If R20 is not zero, go back to AGAIN.
RET: Return from the subroutine.
🧠 Together, this forms a looped delay that takes up time by cycling through NOPs and DEC instructions.
🔹 Stack Initialization
.INCLUDE "M32DEF.INC"
.ORG 0x0
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
This initializes the stack pointer using the highest available RAM address (RAMEND) for ATmega32. It’s
mandatory when you use CALL or any stack-based operation.
🔹 PORTC as Output
LDI R16,0xFF
OUT DDRC,R16
LDI R16, 0xFF: Load 11111111 into R16.
OUT DDRC, R16: This sets all 8 bits of PORTC as output.
🔹 Bit-by-Bit Setting of PORTC
SBI PORTC,0
CALL DELAY
SBI PORTC,1
CALL DELAY
...
SBI PORTC,7
CALL DELAY
SBI (Set Bit in I/O Register): Sets a single bit in PORTC.
So, PORTC bits are turned on one by one (i.e., 0 → 1 → 2 ... up to 7), with a delay in between using
CALL DELAY.
💡 This creates an LED running effect if LEDs are connected to PORTC.
🔹 Delay Subroutine
.ORG 0x300
DELAY:
LDI R20,0x05
AGAIN:
NOP
NOP
DEC R20
BRNE AGAIN
RET
A basic delay loop using register R20. It runs a few NOPs (no operations) and decrements R20 until it
reaches 0.
🎓 Viva Questions with Answers
1. What is the purpose of .INCLUDE "M32DEF.INC"?
Ans: It includes predefined register names and constants for the ATmega32 microcontroller to make the
code readable and hardware-specific.
2. Why do we set the stack pointer before calling a subroutine?
Ans: The stack pointer is used to store the return address during a subroutine call (CALL). If not initialized,
the return address may be corrupted.
3. What does LDI R16, 0xFF and OUT DDRC, R16 do?
Ans: This sets all pins of PORTC as outputs, since 0xFF = 11111111 in binary.
4. What is the function of SBI PORTC,0?
Ans: It sets (turns ON) bit 0 of PORTC, making the corresponding pin output a high voltage (usually 5V).
5. What is the difference between SBI and OUT instructions?
Ans: SBI sets one specific bit of an I/O register, while OUT writes an entire 8-bit value to a register.
6. Why is a delay used after each SBI?
Ans: Without a delay, all bits would be set too quickly for any visual change. The delay helps in creating a
visible effect, such as sequential LED lighting.
7. How does the DELAY subroutine work?
Ans: It uses register R20 to run a loop of NOPs and decrements until zero, providing a simple time delay.
8. What is the effect of this code on PORTC pins?
Ans: It sets each bit of PORTC one by one with a delay, useful for an LED chasing effect where LEDs light
up in sequence.
9. What would happen if DDRC is not configured?
Ans: PORTC pins would remain in their default input mode, and setting bits in PORTC might not produce
the expected output signal.
10. Why is .ORG 0x300 used before the DELAY subroutine?
Ans: To place the DELAY subroutine at a specific memory location (0x300), keeping it separate from the
main program for organized memory layout.
🧠 What this program does:
Sets PORTC pin 2 (PC2) high (using SBI).
Waits for a short delay.
Then clears PC2 (using CBI).
Waits again.
Repeats this loop forever.
💡 Outcome: PC2 keeps toggling ON and OFF with a delay — commonly used to blink an LED
connected to PC2.
🧠 Code Breakdown
🔸 Initialization of Stack Pointer
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
This sets up the stack pointer, a required step when using subroutines like CALL.
🔸 PORTC Setup
SBI DDRC,2
Sets PC2 as an output pin.
🔸 Main Loop (HERE: Label)
HERE:
SBI PORTC,2 ; Set PC2 high
CALL DELAY ; Wait
CBI PORTC,2 ; Clear PC2 (set low)
CALL DELAY ; Wait
RJMP HERE ; Repeat
Simple blinking behavior using subroutines for delay.
🔸 Delay Subroutine
.ORG 0x300
DELAY:
LDI R20,0x05
AGAIN:
NOP
NOP
DEC R20
BRNE AGAIN
RET
Creates a basic time delay by looping through decrement and no-op instructions.
LDI R20, 0b010111001
o LDI stands for "Load Immediate". This instruction loads the 8-bit immediate value
0b010111001 (binary representation) into register R20. Notice there are 9 bits here, which
might be a typo. Assuming it's an 8-bit value, let's consider it as 0b01011100.
LDI R21, 8 ;set a counter to 8
o This instruction loads the immediate value 8 into register R21. This register is initialized as a
counter, likely representing the number of bits in the value we're examining (assuming an 8-
bit value in R20). The comment clarifies its purpose.
LDI R22, 0 ;set number of zeros to 0
o This instruction loads the immediate value 0 into register R22. This register seems to be
intended to store the count of zero bits encountered, although the subsequent logic counts set
bits.
AGAIN: ROL R20 ;move MSB to Carry
o AGAIN: is a label, marking this point in the code. ROL stands for "Rotate Left through
Carry". This instruction rotates the bits in register R20 one position to the left. The most
significant bit (MSB) of R20 is moved into the Carry flag of the Status Register (SREG), and
the Carry flag's previous value is moved into the least significant bit (LSB) of R20.
BRCS NEXT ;branch if set carry
o BRCS stands for "Branch if Carry Set". This is a conditional branch instruction. If the Carry
flag in the SREG is set (meaning the MSB of R20 before the rotation was a '1'), the program
execution will jump to the instruction labeled NEXT.
INC R22 ;increment number of zeros
o INC stands for "Increment". If the Carry flag was not set (meaning the MSB of R20 was '0'),
this instruction is executed, incrementing the value in register R22. Based on the comment,
this is intended to count zeros, but the branch condition suggests the logic is designed to
count ones.
NEXT: DEC R21 ;decrement the counter
o NEXT: is another label. DEC stands for "Decrement". This instruction decrements the value
in register R21 (our counter).
BRNE AGAIN
o BRNE stands for "Branch if Not Equal to Zero". This is another conditional branch
instruction. If the value in register R21 is not zero, the program execution will jump back to
the instruction labeled AGAIN.
Functionality:
This code effectively iterates through the bits of the value initially loaded into R20. In each iteration:
1. It rotates the bits of R20 to the left, moving the MSB into the Carry flag.
2. It checks the Carry flag.
3. If the Carry flag is set (MSB was '1'), it proceeds to the NEXT label and decrements the counter.
4. If the Carry flag is clear (MSB was '0'), it increments R22 (intended as a zero counter, but logically
acting on zero bits) and then proceeds to NEXT to decrement the counter.
5. The loop continues until the counter in R21 becomes zero, meaning all 8 bits (as initially set in R21)
have been processed.
The net effect of this code is to:
Count the number of '1' bits in the initial value of R20. This count will effectively be 8 - (final value
of R22).
The register R22 will hold the count of '0' bits in the initial value of R20 (based on the logic).
void delay_500us ();
o This is a function declaration (prototype) for a function named delay_500us that takes no
arguments and returns no value (void). This tells the compiler about the existence and
signature of this function before it's actually defined.
int main ()
o This is the main function, the entry point of the program execution. It returns an integer value
(typically 0 to indicate successful execution).
o DDRA = DDRA | (1 << PA0);
DDRA stands for Data Direction Register for Port A. Each bit in this register controls
the direction (input or output) of the corresponding pin on Port A.
(1 << PA0) creates a bitmask where only the bit corresponding to pin PA0 is set to 1.
PA0 is a symbolic name (likely defined in avr/io.h) representing the 0th pin of Port A.
The bitwise OR operator (|) is used to set the corresponding bit in DDRA to 1, making
pin PA0 of Port A an output pin. The other bits of DDRA are left unchanged.
o while(1)
This creates an infinite loop. The condition 1 is always true, so the code inside this
loop will execute continuously.
PORTA = PORTA ^ (1 << PA0);
PORTA is the Data Register for Port A. Writing to this register controls the
output state of the pins configured as outputs in DDRA.
(1 << PA0) again creates a bitmask for pin PA0.
The bitwise XOR operator (^) is used here. XORing a bit with 1 toggles its
value (0 becomes 1, and 1 becomes 0). This line effectively toggles the state of
the PA0 pin on Port A. If the LED is connected to this pin, it will turn on if it
was off, and off if it was on.
delay_500us();
This is a function call to the delay_500us function. This function is intended to
introduce a delay of approximately 500 microseconds.
o return 0;
This statement is technically never reached because of the while(1) loop. However, in
a program without an infinite loop, it would indicate successful program termination.
void delay_500us ()
o This is the definition of the delay_500us function.
o TCNT1H = (-500) >> 8;
TCNT1H is the high byte of the 16-bit Timer/Counter 1 register.
(-500): This looks unusual. It's likely intended to calculate a specific timer value for
the delay. However, using a negative value directly isn't the standard way to set timer
counts for delays. It might be relying on some implicit type conversion or overflow
behavior. It's more common to calculate the required timer ticks based on the system
clock frequency and the desired delay.
>> 8 right-shifts the result by 8 bits, effectively taking the higher byte.
o TCNT1L = (-500) & 0xFF;
TCNT1L is the low byte of the 16-bit Timer/Counter 1 register.
(-500) & 0xFF performs a bitwise AND with 0xFF (binary 11111111), which isolates
the lower 8 bits of the (potentially misinterpreted) negative value.
o TCCR1A = 0;
TCCR1A is the Timer/Counter 1 Control Register A. Setting it to 0 configures
Timer/Counter 1 for normal port operation (no PWM or output compare unit actions
in this basic mode).
o TCCR1B = 0x02;
TCCR1B is the Timer/Counter 1 Control Register B. This register controls the clock
source and prescaler for the timer. 0x02 typically selects the system clock divided by
8 as the clock source for the timer.
o TIFR = (1 << TOV1);
TIFR is the Timer/Counter Interrupt Flag Register. TOV1 is the Timer 1 Overflow
Flag bit. Writing a '1' to this bit clears the overflow flag. This ensures that any
previous overflow is cleared before starting the delay.
o while ((TIFR & (1 << TOV1)) == 0);
This is a while loop that continues to execute as long as the Timer 1 Overflow Flag
(TOV1) in the TIFR register is 0.
The timer is configured to count up. When it reaches its maximum value (0xFFFF for
a 16-bit timer) and overflows, the TOV1 flag is set to 1. This loop waits for this
overflow to occur. The number of clock cycles (and thus the time) it takes for the
timer to overflow depends on the initial value in TCNT1H:TCNT1L and the prescaler.
o TCCR1B = 0x00;
This line stops Timer/Counter 1 by setting the clock source selection bits in TCCR1B
to 0.
o TIFR = (1 << TOV1);
This line clears the Timer 1 Overflow Flag again.
Function Calls:
In this code, there is one explicit function call:
delay_500us(); is called within the while(1) loop in the main function. This means that after toggling
the LED, the program will execute the code within the delay_500us function, causing a pause before
the LED is toggled again.
In essence, the program does the following:
1. Configures pin PA0 of Port A as an output.
2. Enters an infinite loop.
3. Toggles the state of pin PA0 (turning the LED on or off).
4. Calls the delay_500us function, which uses Timer/Counter 1 to create a delay of approximately 500
microseconds by waiting for the timer to overflow.
5. Repeats from step 3.
void T1Delay ();
o Function declaration for the delay function, now named T1Delay, taking no arguments and
returning nothing.
int main ()
o The main function.
o DDRB = 0xFF;
DDRB is the Data Direction Register for Port B. Setting it to 0xFF (binary 11111111)
configures all pins of Port B as output pins.
o while(1)
The infinite loop for continuous execution.
PORTB = PORTB ^ (1 << PB4);
PORTB is the Data Register for Port B.
(1 << PB4) creates a bitmask where only the bit corresponding to pin PB4 is
set to 1.
The XOR operator (^) toggles the state of the PB4 pin on Port B. If an LED is
connected to this pin, it will blink.
T1Delay();
This calls the T1Delay function to introduce a pause.
o return 0;
Unreachable due to the while(1) loop.
void T1Delay ()
o Definition of the T1Delay function.
o TCNT1H = 0xC1;
Sets the high byte of Timer/Counter 1 (TCNT1H) to the hexadecimal value 0xC1.
This is part of the initial value loaded into the 16-bit timer.
o TCNT1L = 0x80;
Sets the low byte of Timer/Counter 1 (TCNT1L) to the hexadecimal value 0x80.
Together with TCNT1H, the initial value of the timer is 0xC180.
o TCCR1A = 0x00;
Timer/Counter 1 Control Register A is set to 0x00, configuring Timer 1 for normal
port operation.
o TCCR1B = 0x01;
Timer/Counter 1 Control Register B is set to 0x01. This configures the clock source
for Timer 1 to be the system clock with no prescaling (clock frequency divided by 1).
o TIFR = (1 << TOV1);
Clears the Timer 1 Overflow Flag (TOV1) in the Timer/Counter Interrupt Flag
Register (TIFR). This ensures a clean start.
o while ((TIFR & (1 << TOV1)) == 0);
This loop waits for the Timer 1 Overflow Flag (TOV1) to be set. The timer counts up
from its initial value (0xC180) until it reaches its maximum value (0xFFFF) and
overflows, setting the TOV1 flag.
o TCCR1B = 0x00;
Stops Timer/Counter 1 by setting the clock source selection bits in TCCR1B to 0.
o TIFR = (1 << TOV1);
Clears the Timer 1 Overflow Flag again.
Function Calls:
Similar to the previous code, the primary function call is:
T1Delay(); within the while(1) loop in the main function. This introduces the delay between each
toggle of the LED connected to PB4.