How do I convert this program to assembly from c++
The obvious solution for converting any program to assembly is... to
compile it! You can disassemble the resulting binary with a command
like:

avr-objdump -S -C the_program.elf &gt; the_program.lss


This will output a listing of the program, with the original source
interspersed with an hex dump of the binary and the corresponding
disassembly.


  The part I am really stuck on is the analogRead() function.


Here it goes:

000003a6 &lt;analogRead&gt;:
 3a6:   8e 30           cpi r24, 0x0E   ; 14
 3a8:   08 f0           brcs    .+2         ; 0x3ac &lt;analogRead+0x6&gt;
 3aa:   8e 50           subi    r24, 0x0E   ; 14
 3ac:   87 70           andi    r24, 0x07   ; 7
 3ae:   20 91 00 01     lds r18, 0x0100 ; 0x800100 &lt;__data_start&gt;
 3b2:   90 e4           ldi r25, 0x40   ; 64
 3b4:   29 9f           mul r18, r25
 3b6:   90 01           movw    r18, r0
 3b8:   11 24           eor r1, r1
 3ba:   82 2b           or  r24, r18
 3bc:   80 93 7c 00     sts 0x007C, r24 ; 0x80007c &lt;__TEXT_REGION_LENGTH__+0x7e007c&gt;
 3c0:   80 91 7a 00     lds r24, 0x007A ; 0x80007a &lt;__TEXT_REGION_LENGTH__+0x7e007a&gt;
 3c4:   80 64           ori r24, 0x40   ; 64
 3c6:   80 93 7a 00     sts 0x007A, r24 ; 0x80007a &lt;__TEXT_REGION_LENGTH__+0x7e007a&gt;
 3ca:   80 91 7a 00     lds r24, 0x007A ; 0x80007a &lt;__TEXT_REGION_LENGTH__+0x7e007a&gt;
 3ce:   86 fd           sbrc    r24, 6
 3d0:   fc cf           rjmp    .-8         ; 0x3ca &lt;analogRead+0x24&gt;
 3d2:   80 91 78 00     lds r24, 0x0078 ; 0x800078 &lt;__TEXT_REGION_LENGTH__+0x7e0078&gt;
 3d6:   20 91 79 00     lds r18, 0x0079 ; 0x800079 &lt;__TEXT_REGION_LENGTH__+0x7e0079&gt;
 3da:   90 e0           ldi r25, 0x00   ; 0
 3dc:   92 2b           or  r25, r18
 3de:   08 95           ret


Now, in order to more easily make sense of it, you need to know what all
those magic numbers mean. The command

avr-nm -nC the_program.elf


will dump the symbols associated with every RAM and flash address. In
order to tell those apart, 0x800000 is added to every RAM address. This
will tell you that, in this specific program, the RAM address 0x0100
is actually the address of the variable analog_reference from the
Arduino core library. Thus, you can replace the line

 3ae:   20 91 00 01     lds r18, 0x0100 ; 0x800100 &lt;__data_start&gt;


by

    lds  r18, analog_reference


Then you can use the datasheet to get the names of the memory-mapped IO
registers, and the names of the bits from those registers. I have done
all this for you, so here is the cleaned-up disassembly of
analogRead():

analogRead:
    cpi  r24, 14
    brcs 1f
    subi r24, 14
1:  andi r24, 0x07
    lds  r18, analog_reference
    ldi  r25, 1&lt;&lt;REFS0
    mul  r18, r25
    movw r18, r0
    clr  r1
    or   r24, r18
    sts  _SFR_MEM_ADDR(ADMUX), r24
    lds  r24, _SFR_MEM_ADDR(ADCSRA)
    ori  r24, 1&lt;&lt;ADSC
    sts  _SFR_MEM_ADDR(ADCSRA), r24
2:  lds  r24, _SFR_MEM_ADDR(ADCSRA)
    sbrc r24, ADSC
    rjmp 2b
    lds  r24, _SFR_MEM_ADDR(ADCL)
    lds  r18, _SFR_MEM_ADDR(ADCH)
    ldi  r25, 0
    or   r25, r18
    ret


If you compare this with the source code of analogRead(), it
should now make perfect sense.


  I am trying to implement a program in assembly


Now, if you want to write the assembly yourself, that's a whole
different story. If you allow yourself the use of the Arduino core, then
you can simply

    ldi r24, 14  ; pin A0 is pin 14
    call analogRead


If you don't want to rely on external libraries, and really write
everything yourself, it's going to be a lot harder. I suggest you do
this as a two-step process:

Step 1: Learn how to program the ATmega328P in plain C, at the
register level, using no library other than the avr-libc. You can start
by reading this short tutorial about direct port access.
But then you should quickly move on to studying the
datasheet and the manual of the avr-libc. Yes,
that's a lot to digest. I hope you didn't expect this to be quick and
easy. But note that, on those documents, you can skip all the chapters
related to functions you are not going to use.


  I am really stuck on is the analogRead() function.


In the specific case of your example program, you don't need a version
of analogRead() that can read any pin with any reference. You can
instead write a simpler version that just reads the currently selected
pin using the currently selected reference:

int simple_analog_read(void)
{
    ADCSRA |= 1&lt;&lt;ADSC;                    // start the converion
    while (ADCSRA &amp; (1&lt;&lt;ADSC)) continue;  // wait till it's done
    return ADC;                           // return the reading
}


Of course, this assumes you have appropriately initialized the ADC.


  int voltage = sensorValue * (5.0 / 1023.0);


You really, really don't want to implement something like this in
assembly. The AVR hardware supports neither floating point nor
divisions. All this is handled by the compiler through supporting
libraries, and they are not simple. What you really want is something
like

int voltage = (sensorValue * 5) &gt;&gt; 10;


which is incidentally more correct, as the scaling factor is 5/1024, not
5/1023 (see the datasheet).

Step 2: Learn assembly, and rewrite your low-level C code in
assembly. If your C code is low-level enough this should not be too
hard. As a learning aid, you can ask the compiler to do it for you and
then try to make sense of what it did. If you just try to compile the
simple_analog_read() function I wrote above you will see the
translation to assembly is almost trivial.