AVR Assembly Programming Problems

Programming Problems

  • The following programming problems are designed for the Arduino Uno (ATmega328P) with the CSULB Shield. Programs may be written in AVR Studio 4 or Atmel Studio 6.
  • To display code, open in Chrome browser or download (Firefox).

1) In this programing problem you will write the assembly code needed to display a number between 0 and 9 as defined by the least significant 4 switches on your proto-shield (PINC). If the number is greater than 9 turn ON the discrete LED wired to PORTB bit 0, otherwise turn OFF the LED.

I have written much of the code, including calls to subroutines InitShield, WriteDisplay and BCD_to_7SEG. You should be familiar with the first two from your Lab work. The BCD_to_7SEG subroutine takes as input a number between 0 and 9 in register r0. The subroutine then converts the decimal number into its corresponding 7 segments and displays answer.

As you write your program remember that:

  • The least significant 4 switches are wired to PINC.
  • The error LED is wired to PORTB bit 0
  • BCD_to_7SEG’s calling argument is in register r0
  • Do not modify r16 when you check to see if it is less than 10

.INCLUDE 
RST_VECT:
   rjmp   reset
.ORG 0x0100
.INCLUDE "spi_shield.inc"
reset:
   ldi    r16,low(RAMEND)
   out    SPL,r16
   ldi    r16,high(RAMEND)
   out    SPH,r16
; Initialize Proto-shield
   call   InitShield
loop:
   _____    r16, _____   // Read Switches from GPIO Registers
   _____    r16, 0x___   // clear most significant nibble
   _____    r16, 0x___   // Is r16 less than 1010? (see notes)
   _____   ___________   // unsigned conditional branch
   _____   ______, ___   // error - turn on the LED
   rjmp    ___________   // see the flowchart
no_error:
   _____   ______, ___    // not an error - turn off the LED
display:
   _____   ____, _____    // send argument to subroutine
   call   BCD_to_7SEG     // (see notes)
   call   WriteDisplay
   rjmp    ____________

2) Write a subroutine named BlinkIt to complement a variable named blink and to then send the least significant bit (b0)of blink, in SREG bit T, to a subroutine named TestIt. For the purpose of this exam you do not need to save registers on the stack for this question.

.DSEG
Blink:  .BYTE   1
.CSEG
BlinkIt:
    ______   _________      // load variable to register 16
    ______   _________      // do something
    ______   _________      // store register 16 back to variable
    ______   _________      // store bit 0 to SREG T bit
    ______   _________      // call TestIt using relative addressing mode
    ret

Solution

Code

; ----------------------------------------
; BlinkIt - TestIt
; Version 1.0
; Date: 10/24/2014
; Written By : Khoi Vu
; ----------------------------------------

.INCLUDE 

.DSEG
blink: .BYTE 1

.CSEG
.ORG 0x0000
RST_VECT:
rjmp reset 
.ORG 0x0100 
.INCLUDE "spi_shield.inc"

reset:
call InitShield 
clr spiLEDS // clear discrete LEDs
test:
rcall BlinkIt
rjmp test


BlinkIt:
lds r16,blink
com r16 // complement r16, since blink=0x00, output= 0xFF
sts blink,r16 // store back to blink
bst r16,0 // store bit 0 of r16 to T
rcall TestIt // call TestIt
ret

TestIt:
mov r16,spiLEDS // move LEDs to register 16
sbr r16,0b10000000 // guessing LED is on
brts done // since bit 0 of r16 was 1 T=1 so brts is set. it will branch to done
cbr r16,0b10000000 // clear r16 if not on
done:
mov spiLEDS,r16 // move r16 back to LED
rcall WriteDisplay // output to display
ret
Simulation

Solution Package

3) The following flowchart defines a subroutine named TestIt which is called by BlinkIt. TestIt therefore accepts the T bit as an argument. We are working with the Arduino Proto-shield (see Proto-shield Schematic). Translate the following flowchart into its equivalent AVR code. Your code must implement the flowchart on the right. For the purpose of this exam you do not need to save any registers on the stack for this question.

.DEF  spiLEDS = r9
TestIt:
______   _________
______   _________
______   _________    ; guess bit is set
;execute next line only if t = 0
______   _________    ; guess is wrong
done:
______   _________
______   _________
ret

Solution

Code

; ----------------------------------------
; BlinkIt - TestIt
; Version 1.0
; Date: 10/24/2014
; Written By : Khoi Vu
; ----------------------------------------

.INCLUDE 

.DSEG
blink: .BYTE 1

.CSEG
.ORG 0x0000
RST_VECT:
    rjmp reset                
.ORG 0x0100                
.INCLUDE "spi_shield.inc"

reset:
	call InitShield           
	clr  spiLEDS	// clear discrete LEDs
test:
	rcall BlinkIt
	rjmp  test


BlinkIt:
	lds r16,blink
	com r16			// complement r16, since blink=0x00, output= 0xFF
	sts blink,r16	// store back to blink
	bst r16,0		// store bit 0 of r16 to T
	rcall TestIt	// call TestIt
    ret

TestIt:
	mov r16,spiLEDS		// move LEDs to register 16
	sbr r16,0b10000000	// guessing LED is on
	brts done			// since bit 0 of r16 was 1 T=1 so brts is set. it will branch to done
	cbr	r16,0b10000000	// clear r16 if not on
done:
	mov spiLEDS,r16		// move r16 back to LED
	rcall WriteDisplay	// output to display
    ret
Simulation

Solution Package

4) Given variables A and B, each holding an 8-bit signed 2’s complement number. Write a program to find the maximum value and put into variable C. Example if A > B then C = A.

Option A: Basic implementation of if-then-else statement using load -> do something -> store structure

Solution

Code

/* Given variables A and B, each holding an 8-bit signed 2's complement number, 
 * write a program to find the maximum value and put into variable C. For 
 * example if A > B then C = A.
 *
 * Solution A: Basic implementation of if-then-else statement
 * using load -> do something -> store structure
 */ 
.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1

.CSEG
Max1:
	lds  r16,A    ; load
	lds  r17,B
	cp   r16,r17
	brlt elseMax1 ; if (A >= B)  note: if A < B branch to else block
	mov  r18,r16  ; then C = A
	rjmp endMax1
elseMax1: 
    mov  r18,r17
	sts  C,r18    ; store
endMax1:
    rjmp Max1
Simulation

Solution Package

Option B: Basic implementation of if-then-else statement. Structure modified to immediately

Solution

Code

/* Given variables A and B, each holding an 8-bit signed 2's complement number, 
 * write a program to find the maximum value and put into variable C. For 
 * example if A > B then C = A.
 *
 * Solution B: Basic implementation of if-then-else statement.
 * Structure modified to immediately store result.
 */
.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1

.CSEG
Max2:
	lds  r16,A    ; load
	lds  r17,B
	cp   r16,r17
	brlt elseMax2 ; if (A >= B)
	sts  C,r16    ; then C = A
	rjmp endMax2
elseMax2:
    sts  C, r17
endMax2:
Simulation

Solution Package

Option C: If-then-else statement restructured to if-then with guess. Result immediately stored in SRAM.

Solution

Code

/* Given variables A and B, each holding an 8-bit signed 2's complement number, 
 * write a program to find the maximum value and put into variable C. 
 * For example if A > B then C = A.
 *
 * Solution C: if-then-else statement restructured to if-then with guess 
 * Result immediately stored in SRAM.
 */
 .INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1

.CSEG

Max3:
	lds  r16, A    ; load
	lds  r17, B
	sts  C, r16    ; guess A > B
	cp   r16, r17
	brge endMax3
	sts  C, r17
endMax3:
    rjmp Max3
Simulation

Solution Package

5) Given variable A holds an 8-bit signed 2's complement number. Write a program to find the absolute value A. Save result back into variable A.

A = |A|

Solution

Code

/* Program 2 Given variable A holds an 8-bit signed 2's complement number, 
 *           write a program to find the absolute value A. 
 *           Save result back into variable A.
 *           A = |A|
 */

.INCLUDE  

.DSEG
A:     .BYTE 1

.CSEG
Absolute:
	lds  r16, A    ; load
	tst  r16       ; if (A < 0)
	brpl endAbs
	neg  r16       ; then convert to a positive number
endAbs:
    sts  A, r16    ; store
    rjmp Absolute
Simulation

Solution Package

6) Write a program to add 8 bit variables A and B together. Store the sum into 8 bit variable C. For this programming problem you may assume that the sum is less than 255 if A and B are unsigned and between -128 and 127 if signed.

C = A + B

Solution

Code

/* Write a program to add 8 bit variables A and B together,
 * and storing the sum into 8 bit variable C.
 * For this programming problem you may assume that the sum is less
 * than 255 if A and B are unsigned and between -128 and 127 if signed.
 * C = A + B
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1

.CSEG
Adder88:
	lds  r0,A    ; load
	lds  r2,B
	add  r0,r2   ; add
	sts  C,r0    ; store
	rjmp Adder88
Simulation

Solution Package

7) Write a program to find the sum of unsigned 8 bit variables A and B. For this programming problem the sum may be greater than 255 if A and B. Store the sum into 16 bit variable C using little endian byte ordering.

C = A + B

Solution

Code

/* Write a program to find the sum of 8 bit variables A and B.
 * For this programming problem the sum may be greater than 255 if A and B
 * are unsigned or less than -128 and greater than 127 if signed.
 * Store the sum into 16 bit variable C using little endian byte ordering.
 * C = A + B
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 2

.CSEG
Adder816:
    ; load
    clr  r1       ; r1:r0 = 0:A
	lds  r0,A
	clr  r3       ; r3:r2 = 0:B
	lds  r2,B
	; add
	add  r0,r2    ; add least significant bytes
    adc  r1,r3    ; add with carry most significant bytes
	; store
	sts  C,r0     ; store least significant byte first
	sts  C+1,r1
	rjmp Adder816
Simulation

Solution Package

8) Write a program to find the sum of signed 8 bit variables A and B. For this programming problem the sum may be less than -128 and greater than 127. Store the sum into 16 bit variable C using little endian byte ordering.

C = A + B

Solution

Code

/* Write a program to find the sum of 8 bit variables A and B.
 * For this programming problem the sum may be less than -128 and greater than 127
 * Store the sum into 16 bit variable C using little endian byte ordering.
 * C = A + B
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 2

.CSEG
Adder816s:
    ; load
    clr  r17       ;  0:A
	lds  r16,A     ; First 8 bits are A
	clr  r19       ;  0:B
	lds  r18,B     ; First 8 bits are B
	; make variables 16-bit
	sbrc r16,7
	ser  r17
	sbrc r18,7
	ser  r19
	;add
	add  r16,r18
	adc  r17,r19
	;store
	sts  C,r16     ; store the least significant byte
	sts  C+1,r17   ; store most significant bytes
	rjmp Adder816s
Simulation

Solution Package

9) Multiply 8-bit unsigned variables A and B placing the product into 16-bit variable C. Save the 16-bit product using little endian byte ordering.

C = A x B

Solution

Code

/* Multiply 8-bit unsigned variables A and B placing
 * the product into 16-bit variable C. 
 * Save the 16-bit product using little endian byte ordering.
 * C = A x B 
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 2

.CSEG
Mul8x8_16:
    lds  r16,A    ; load
	lds  r17,B
	mul  r16,r17
	sts  C,r0     ; least significant byte (little end)
	sts  C+1,r1   ; most  significant byte (big end)

	rjmp Mul8x8_16
Simulation

Solution Package

10) Given 8-bit variables A and B, each holding an 8-bit unsigned Write a program to find the average of A and B. Place the result into variable C.

Hint: Shifting (or rotating) a binary number to the left divides the number by 2.

Solution

Code

/* Given 8-bit unsigned variables A and B, each holding an 8-bit signed 2's complement number, 
 * write a program to find the average of A and B and put the result into variable C.
 * Hint: Shifting (or rotating) a binary number to the left is equivalent to dividing by 2.  
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1

.CSEG
Avg:
	lds  r16, A    ; load
	lds  r17, B
	add  r16, r17  ; add
    ror  r16       ; divide by 2 (include carry)
	sts  C, r16    ; store
	rjmp Avg
Simulation

Solution Package

11) Given 8-bit variables A and B, each holding an 8-bit signed 2's complement number. Write a program to find the average of A and B. Place the result into variable C.

Hint: Shifting (or rotating) a binary number to the left divides the number by 2.

Solution

Code

/* Given 8-bit variables A and B, each holding
 * an 8-bit signed 2's complement number. Write
 * a program to find the average of A and B.
 * Place the result into variable C.
 */

.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 2

.CSEG

; inputs: 8-bit variables A and B
; output: 16-bit register C
Avg8s:
    ; load registers A and B
	lds  r24,A
	lds  r26,B
	; find average C = A+B/2
	rcall Adder816s		; C=A+B
	;   divide by 2
	asr r25             ; least significant bit moved to carry bit C
	ror r24		    	; carry moves into most significant bit of r24
	; store the 8 bit result
	sts  C,r24     	
	clr  r25    
	sts C+1,r25	
	rjmp Avg8s

; Add two 8-bit signed 2's complement numbers,
; where sum of A and B may be 9 bits
; input: r24 and r26 are two 8-bit numbers
; output: register pair r25:r24 equals sum of r24 and r25
Adder816s:
	; make variables 16-bit
	clr  r25      	 	; guess r25 is positive 0x00:A
	sbrc r24,7          ; if number is positive guess is correct so skip next instruction
	ser  r25            ; guess incorrect, number is negative 0xFF:A
	clr  r27
	sbrc r26,7
	ser  r27
	;add
	add  r24,r26
	adc  r25,r27
	;store
	sts  C,r24     		; store the least significant byte
	sts  C+1,r25  		; store most significant bytes               
	ret
Simulation

Solution Package

12) Write a function named Div8_8 to divide an unsigned 8 bit number by an unsigned 8 bit number. You can find this program in your textbook (Mazidi). Test your function by writing a program named Div8_8test to test the subroutine Div8_8 by dividing the 8-bit-number: 0xAA by the 8-bit-number 0x55.

Solution

Code

; Div8_8

; Version 1.0
; Date: November 11, 2014
; Written By : Yoseph Yegezu
; From text book 'The AVR Microcontroller and Embedded systems'Chapter 5 Page 167

.INCLUDE 

			
.CSEG

.DEF Num=R20
.DEF Denominator=R21
.DEF Quotient=R22

.ORG 0x0000

	ldi Num, 0xAA		  
	ldi Denominator, 0x55	  
	//call the 8 bit division
    rcall Div8
    ret	
/************************************
 * subroutine divides unside 8bit by 8bit
 * Quotient = Numerator/Denominator *
 *     r22  =     r20 / r21  
 * with remainder in r20            *
 ************************************/

Div8: 
    clr  Quotient          // r22

// quotient is going to increment by 1 everytime L1 loops
// loop  L1 stops when the numerator-denominator = less than the demoninator

L1:
	inc Quotient
	
	sub  Num,Denominator  // r20,r21
	brcc L1

//since the quotient is incremented by 1 when the loop began, after the loop quotient is dec
	dec  Quotient

//notice L1 is going to branch off when the numerator is no lnger divisiable by the denominator 
//which means L1 is branching off when numerator-denominator results in a negative value.
//therefore, the denominator is going to be added to the numerator after the loop.
	add  Num,Denominator  // r20,r21
	
	ret
Simulation

Solution Package

13) Write a function named Div16_8 to divide an unsigned 16 bit number by an unsigned 8 bit number. Test your function by writing a program named Div8_test to test the subroutine Div16_8 by dividing the 16-bit-number: 0xAAAA by the 8-bit-number 0x55.

Option A

Solution

Code

/*
 * Write a subroutine named Div8 to divide a 16-bit number by an 8-bit number.  
 * Next, write a program named Div8_test to test the subroutine Div8
 * by dividing the 16-bit-number: 0xAAAA by the 8-bit-number 0x55
 *
 * Q = N/D Divide a 16-bit-number NH:NL  by an 8-bit-number Q
 *
 * Source: 
 * 1. Binary division in AVR Assembler
 *    http://www.avr-asm-tutorial.net/avr_en/calc/DIVISION.html
 * 2. Integer division (unsigned) with remainder
 *    http://en.wikipedia.org/wiki/Division_algorithm
 */

.DEF NL = r0  ; LSB 16-bit-number to be divided
.DEF NH = r1  ; MSB 16-bit-number to be divided
.DEF DIV = r3 ; 8-bit-number to divide with
.DEF QL = r4  ; LSB result
.DEF QH = r5  ; MSB result

Div8_test:
	ldi r16,0xAA ; 0xAAAA to be divided
	mov NH,r16
	mov NL,r16
	ldi r16,0x55 ; 0x55 to be divided with
	mov DIV,r16
	rcall Div8	
	rjmp Div8_test
 
 /* Div8
  * Q = N/D Divide a 16-bit-number NH:NL  by an 8-bit-number Q
  * input
  *   N = Numerator (dividend)
  *   D = Denominator (divisor)
  * output
  *   Q = Quotient 
  */
Div8:
    push r0
	push r1
	push r2

	clr r2 ; clear interim register
	clr QH ; QH:QL = 0b0000 0000 0000 0001
	clr QL
	inc QL

div8a:         ; start of the division loop
	clc        ; clear carry-bit
	rol NL     ; rotate the next-upper bit of the numerator
	rol NH     ; to the interim register (multiply by 2)
	rol r2
	brcs div8b ; a one has rolled left, so subtract
	cp r2,DIV  ; Division result 1 or 0?
	brcs div8c ; jump over subtraction, if smaller
div8b:
	sub r2,DIV ; subtract number to divide with
	sec        ; set carry-bit, result is a 1
	rjmp div8d ; jump to shift of the result bit
div8c:
	clc        ; clear carry-bit, resulting bit is a 0
div8d:
	rol QL     ; rotate carry-bit into result registers
	rol QH
	brcc div8a ; as long as zero rotate out of the result
	           ; registers QH:QL go on with the division loop
	pop r2
	pop r1
	pop r0
	ret        ; End of the division reached

program8:
Simulation

Solution Package

Option B

This solution is an extension of Div8_8

Solution

Code

; Div16_8B 
; Version 1.0
; Date: November 11, 2014
; Written By : Yoseph Yegezu

.INCLUDE 
		
.CSEG

.DEF Denominator=R19
.DEF NL=r24
.DEF NH=r25
.DEF QL=r21
.DEF QH=r22

.ORG 0x0000

	ldi r16,0xAA
	mov NH,r16
	mov NL,r16
	ldi Denominator, 0x55
	//Call the 16 bit by 8 bit division
    rcall Div16_8
	ret	

/************************************
 * subroutine divides unside 16bit by 8bit
 * Quotient = Numerator/Denominator    *
 * r22:r21  = r25:r24 / r19        *
 ***************************************/
Div16_8: 
  	clr  r22
	clr  r21         


// loop  L1 stops when the numerator - denominator = less than the demoninator

L1:
//QL is going to increment by 1, everytime L1 loops
	inc QL   //r22
	
//When QL reaches 255 or 0XFF and then goes back to 0, QH is going to increment by 1
	cpi r21,0
	brne No_Inc
	inc QH   //r22

	No_Inc:
	sub  r24,Denominator  // r19
	sbc  r25,r2

	brcc L1

//Since r21 is incremented by 1 when the loop began, after the loop r21 is decremented
	dec  QL  //r21

//Notice L1 is going to branch off when the numerator is no lnger divisiable by the denominator 
//Which means L1 is branching off when r24-denominator results in a negative value.
//Therefore, the denominator is going to be added to the r24 after the loop.
	
	add  r24,Denominator  //r24,r19
	adc  r25,r2
	
	
	ret
Simulation

Solution Package

14) Write a subroutine that convert a temperature reading in Fahrenheit (variable F)to Celsius (variable C).

Solution

Code

; ConvertCtoF

; Version 1.0
; Date: November 11, 2014
; Written By : Yoseph Yegezu


.INCLUDE 

.DSEG
		C: .BYTE 1
		F: .BYTE 1			
.CSEG

.DEF Denominator=R19
.DEF Quotient=R22

.ORG 0x0000

//Input a celsius value into r17
TestConvertCtoF:
	ldi r17,74
	sts C, R17
	//call subroutine ConvertCtoF
	rcall ConvertCtoF
	rjmp TestConvertCtoF

/****************************
 * subroutine converts a temperature reading in Celsius (variable C) to Fahrenheit (variable F).
 * F=(C × 9/5) + 32 ==(C × 18/10) + 32
 * Range for C input is from (0 to 124), since F max is 255
 ****************************/
    
ConvertCtoF:
	//Load the C value into r18
	lds r18, C
	//Input the constant value 18 in reg. 16
	ldi r16,18
	//This part calculates (C*18)
	mul r18, r16
	//Move the products into r25H and r24L
	movw r25:r24, r1:r0
	//Input the denominator into r19
	ldi Denominator, 10  //r19,10
	//Call the 16 bit by 8 bit division
    rcall Div16_8
	// add 32 to the quotient (18*C)/10 + 32
    ldi  r26,32
	add  Quotient,r26   //r22,r26
	adc  r23,r2

    //Store the answer into F
	sts  F, r22
	ret	

/************************************
 * Quotient = Numerator/Denominator *
 * r23:r22  = r25:r24 / r19 
 * r24 = remainder                  *
 ************************************/
Div16_8: 
    clr  r2
	clr  r23
	clr  Quotient          // r22

// quotient is going to increment by 1 every time L1 loops
// loop  L1 stops when the numerator-denominator is less than the demoninator(10)
//(18*C)/10
L1:
	inc Quotient
	adc r23,r2

	sub  r24,Denominator  // r24,r19
	sbc  r25,r2

	brcc L1
//since the quotient is incremented by 1 when the loop began, after the loop quotient is dec
	dec  Quotient
	sbc  r23,r2
//notice L1 is going to branch off when the numerator is no lnger divisiable by the denominator. 
//which means L1 is branching off when r24-denominator results in a negative value.
//therefore, the denominator is going to be added to the r24 after the loop.
	add  r24,Denominator
	adc  r25,r2
	
	ret
Simulation

Solution Package

15) Write a subroutine that convert a temperature reading in Celsius (variable C) to Fahrenheit (variable F).

Solution

Code

; ConvertFtoC

; Version 1.0
; Date: November 11, 2014
; Written By : Yoseph Yegezu


.INCLUDE 

.DSEG
		C: .BYTE 1
		F: .BYTE 1			
.CSEG

.DEF Denominator=R19
.DEF Quotient=R22

.ORG 0x0000


// Input a Fahrenheit value into r17
TestConvertFtoC:
	ldi r17,124
	sts F, R17
// Call subroutine ConvertFtoC
	rcall ConvertFtoC
	rjmp TestConvertFtoC

/****************************
 * Subroutine converts a temperature reading in Fahrenheit (variable F) to Celsius (variable C).
 * (F  -  32)  x  5/9 = C
 * Range of F (32 to 255) therefore Cmax is 123.8
 ****************************/
    
ConvertFtoC:
	//Load the F value into r18
	lds r18, F
	//Input the constant value 18 in reg. 16
	ldi r16, 32
	//This part calculates (°F-32)
	sub r18, r16
	ldi r20, 5
	//This part calculates (°F-32)*5
	mul r18, r20
	//Move the products into r25H and r24L
	movw r25:r24, r1:r0
	//Input the denominator into r19
	ldi Denominator, 9
	//Call the 16 bit by 8 bit division
    rcall Div16_8
    sts  C, r22
	ret	

/************************************
 * Quotient = Numerator/Denominator *
 * r23:r22  = r25:r24 / r19         *
 ************************************/
Div16_8: 
    clr  r2
	clr  r23
	clr  Quotient          // r22

// Quotient is going to increment by 1 everytime L1 loops
// Loop  L1 stops when the numerator-denominator = less than 10(the demoninator)
// This part calculates (°F-32)*5/9
L1:
	inc Quotient
	adc r23,r2

	sub  r24,Denominator  // r19
	sbc  r25,r2

	brcc L1
// Since the quotient is incremented by 1 when the loop began, after the loop quotient is dec
	dec  Quotient
	sbc  r23,r2
// Notice L1 is going to branch off when the numerator is no lnger divisiable by the denominator. 
// Which means L1 is branching off when r24-denominator results in a negative value.
// Therefore, the denominator is going to be added to the r24 after the loop.
	add  r24,Denominator
	adc  r25,r2

	ret
Simulation

Solution Package

16) Given variables A, B, and C; each holding an 8-bit unsigned number. Write a program to find the average of A to C, placing the result into variable D.

D = A + B + C / 3

Allow for a 16-bit interim sum and result.

Solution

Code

/* Given variables A, B and C, each holding an 8-bit signed 2's complement number, 
 * write a program to find the average of A to C, placing the result into variable D.
 * D = A + B + C / 3
 * Allow for a 16-bit interim sum and result.
 * Tip: Copy and paste Div8 subroutine into the AvgABC.asm program.
 */
.INCLUDE  

.DSEG
A:     .BYTE 1
B:     .BYTE 1
C:     .BYTE 1
D:     .BYTE 2

.CSEG
Setup:
	ldi r16, 0x34 ;  52 variable default values
	sts A, r16
	ldi r16, 0x78 ; 120
	sts B, r16
	ldi r16, 0xBC ; 188
	sts C, r16    ; sum = 0x168 (360), average = 0x78 (120) 

AvgABC:
               ; load
	clr  r1    ; r1:r0 = 0:C
	lds  r0,A
	clr  r3    ; r3:r2 = 0:B
	lds  r2,B
	clr  r5    ; r5:r4 = 0:A
	lds  r4,C

    add r0,r2  ; A = A + B
    adc r1,r3
	add r0,r4  ; A = A + C 
	adc r1,r5
               ; numerator r1:r0   = A + B + C
	ldi r16,3  ; divisor       /r2 =          /3
	mov r3,r16
	rcall Div8 ; quotient  r4:r3 = r1:r0 / r2 

	sts D,r4
	rjmp AvgABC

/* Div8
 * Q = N/D Divide a 16-bit-number NH:NL  by an 8-bit-number Q
 * input
 *   N = Numerator (dividend)
 *   D = Denominator (divisor)
 * output
 *   Q = Quotient 
 */

.DEF NL = r0  ; LSB 16-bit-number to be divided
.DEF NH = r1  ; MSB 16-bit-number to be divided
.DEF DIV = r3 ; 8-bit-number to divide with
.DEF QL = r4  ; LSB result
.DEF QH = r5  ; MSB result

Div8:
    push r0
	push r1
	push r2

	clr r2 ; clear interim register
	clr QH ; QH:QL = 0b0000 0000 0000 0001
	clr QL
	inc QL

div8a:         ; start of the division loop
	clc        ; clear carry-bit
	rol NL     ; rotate the next-upper bit of the numerator
	rol NH     ; to the interim register (multiply by 2)
	rol r2
	brcs div8b ; a one has rolled left, so subtract
	cp r2,DIV  ; Division result 1 or 0?
	brcs div8c ; jump over subtraction, if smaller
div8b:
	sub r2,DIV ; subtract number to divide with
	sec        ; set carry-bit, result is a 1
	rjmp div8d ; jump to shift of the result bit
div8c:
	clc        ; clear carry-bit, resulting bit is a 0
div8d:
	rol QL     ; rotate carry-bit into result registers
	rol QH
	brcc div8a ; as long as zero rotate out of the result
	           ; registers QH:QL go on with the division loop
	pop r2
	pop r1
	pop r0
	ret        ; End of the division reached
Simulation

Solution Package


More Problems to be added...