Simple write to an element of an array.

Go To Last Post
45 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

Using the Arduino  IDE and an Uno to learn AVR ASM.

 

I HAVE read through the 442 pages of the Datasheet (sheet!? sheet!? Its a friggin BOOK LOL) to the best of my ability. To me it seems the very end of it (of course, why not? LOL) is relevant to my question: the part with the opcodes.

 

I have a simple array / table. I am able to pull up a given element I want and read the value in that location (#26 for this little exercise) and write to the location.

 

I can pull up any value I want. What I can't seem to get to work is to WRITE to that location.

 

 

I have tried ST, and other ways. I even tried to load R24 to R0 then use SPM.   Nothing seems to work.

 

ANY help is appreciated. I suspect it is probably just one or two things i am missing... devil is in the details  LOL

 

 

Thank you all in advance.

 



Table2:   
//   index        0     1       2       3       4       5      6      7  
           .BYTE  10,   21,     32,     43,     54,     65,    76,    87  
           
//                8     9       10      11      12      13     14     15
           .BYTE  98,   109,    110,    121,    132,    143,   154,   165
           
//                16    17      18      19      20      21     22     23           
           .BYTE  176,  187,    198,    209,    216,    221,   232,   243

//                24    25      26      27      28      29     30     31
           .BYTE  1,    2,      3,      4,      5,      6,     7,     8
                              
           
           .BALIGN 2 




#---------------------------------------------------------------------

MyTest:

        LDI      ZL         ,  lo8(   Table2   )   //   Z  at element 0
        LDI      ZH         ,  hi8(   Table2   )   //   Z at element  0   
        ADIW     Z          ,  1                   //   increment: Z is on element #1 (the second position, value of 10)
       

        // This shows how to read / write from the values
        ADIW     Z          ,  25                 //   Z pointer sitting on element # 26 at this point (value of 3)
        LPM      R24        ,  Z                  //   should return the value of 3 found in position # 26
        INC      R24                              //   R24 has  4 in it now.
        INC      R24                              //   R24 has  5 in it now.
        INC      R24                              //   R24 has  6 in it now.



        // *** THIS DOES NOT APPEAR TO WORK! ***
        ST       Z          ,  R24                //   write a value of 6 to position # 26
                                        

        
        LDI      R24       , 77                    
        //RET                                     // verify R24 does not have 6 in it any more

        
        LPM      R24       , Z                     // read from position # 26 again (should have value of 6)
        RET                                        // RET should be a 6, but shows the original value of 3 !


 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where is the table in memory?

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

I used

 

.GLOBAL Table2  

 

 

At the top of my file.

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:

 

 

I used

 

.GLOBAL Table2  

 

 

At the top of my file.

 

That is not an answer to 

Greg_Muth wrote:
Where is the table in memory?

 


 

The 

AVR_Beginner wrote:
I have a simple array / table. I am able to pull up a given element I want and read the value in that location (#26 for this little exercise) and write to the location.   I can pull up any value I want. What I can't seem to get to work is to WRITE to that location.

I cant make sense of that. First sentence says you can write to a location. Second sentence says you can't.

 

Can you re-formulate?

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry:   I miss wrote that.

 

 

I can read any element I wish in the array. I can not write a value to any element. It like the elements are "write protected"... I do not know where in memory the array lives. I just reference what i want with the Z pointed.  I assume Z is working and pointing correctly since I can read and get the correct value... i just can't write a new value to any position in the array.

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If the table is in program memory (accessed via lpm) then you can’t write it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman and everyone else:  Thank you!

 

My next (obvious) question is what can I do to put it in to Data RAM and not Program RAM so I can write and read to it?  I have not see any clear examples how to put a table at runtime / boot up into Data ram.

 

 

Thank you again!

 

 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As part of the startup, the C code zeroes specific areas of ram and copies the initialisation to ram. This means you need to write a function that copies a table from flash into ram to get the same functionality.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman: I am not using C and I do not know any (aside from what little C there is in the Arduino IDE).

 

For the most part, i am doing ASM only. 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This I realise. I was pointing out what C/C++ does for you. Nevertheless, you need to write a function to copy your flash table to ram or write code to populate the table in ram with your values.
You know how to load a pointer and read from flash. Read up on the load and store instructions that read/write to ram. Then make a counted loop. Job done.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
My next (obvious) question is what can I do to put it in to Data RAM and not Program RAM so I can write and read to it?
Already answered in your other thread (Read / write a byte to NON-Register memory) by Cliff in #17.

 

AVR_Beginner wrote:
I have not see any clear examples how to put a table at runtime / boot up into Data ram.
Again, example already given in ...

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman and sternst:  Thank you!  I will go back to the older threads. I am sorry if I repeat my self here... sometimes this stuff is confusing. Sorry :-(

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
Program RAM 

There is no such thing on an AVR!

 

I think you mean either "Flash" or, more generically, "Program memory"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil:

 

Yes... sorry, I used the wrong term.  Yes I mean Flash.

 

It seems I can have a read only table in program / Flash. If I want to have a read /WRITE table or any var i have to load each element in memory 0x20 (memory location 32) and higher: then I can read / write no problem.

 

I am learning... slowly... but consistently.  

 

Thank you everyone for the help!!

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I want to have a read /WRITE table or any var i have to load each element in memory 0x20 (memory location 32) and higher: then I can read / write no problem.

 

You mean read and write with lots of problems, since you will be reading and writing I/O registers.  SRAM begins at 0x0100:

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As others have noted C does a "clever trick" that you may not be aware of. If you write:

#include <avr/io.h>

int main(void) {
	while(1);
}

then when you build that (I picked a small AVR so few interrupt vectors):

C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   0e c0           rjmp    .+28            ; 0x20 <__bad_interrupt>
   4:   0d c0           rjmp    .+26            ; 0x20 <__bad_interrupt>
   6:   0c c0           rjmp    .+24            ; 0x20 <__bad_interrupt>
   8:   0b c0           rjmp    .+22            ; 0x20 <__bad_interrupt>
   a:   0a c0           rjmp    .+20            ; 0x20 <__bad_interrupt>
   c:   09 c0           rjmp    .+18            ; 0x20 <__bad_interrupt>
   e:   08 c0           rjmp    .+16            ; 0x20 <__bad_interrupt>
  10:   07 c0           rjmp    .+14            ; 0x20 <__bad_interrupt>
  12:   06 c0           rjmp    .+12            ; 0x20 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61
  1c:   02 d0           rcall   .+4             ; 0x22 <main>
  1e:   02 c0           rjmp    .+4             ; 0x24 <_exit>

00000020 <__bad_interrupt>:
  20:   ef cf           rjmp    .-34            ; 0x0 <__vectors>

00000022 <main>:
  22:   ff cf           rjmp    .-2             ; 0x22 <main>

00000024 <_exit>:
  24:   f8 94           cli

00000026 <__stop_program>:
  26:   ff cf           rjmp    .-2             ; 0x26 <__stop_program>

So that is what an "almost empty" C program looks like. But look what happens if I add your array to that:

#include <avr/io.h>

uint8_t Table2[] = {
	10,   21,     32,     43,     54,     65,    76,    87,  
	98,   109,    110,    121,    132,    143,   154,   165,
	176,  187,    198,    209,    216,    221,   232,   243,
	1,    2,      3,      4,      5,      6,     7,     8
};

int main(void) {
	while(1);
}

now when I build I get:

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   19 c0           rjmp    .+50            ; 0x36 <__bad_interrupt>
   4:   18 c0           rjmp    .+48            ; 0x36 <__bad_interrupt>
   6:   17 c0           rjmp    .+46            ; 0x36 <__bad_interrupt>
   8:   16 c0           rjmp    .+44            ; 0x36 <__bad_interrupt>
   a:   15 c0           rjmp    .+42            ; 0x36 <__bad_interrupt>
   c:   14 c0           rjmp    .+40            ; 0x36 <__bad_interrupt>
   e:   13 c0           rjmp    .+38            ; 0x36 <__bad_interrupt>
  10:   12 c0           rjmp    .+36            ; 0x36 <__bad_interrupt>
  12:   11 c0           rjmp    .+34            ; 0x36 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_copy_data>:
  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   ee e3           ldi     r30, 0x3E       ; 62
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>
  32:   02 d0           rcall   .+4             ; 0x38 <main>
  34:   02 c0           rjmp    .+4             ; 0x3a <_exit>

00000036 <__bad_interrupt>:
  36:   e4 cf           rjmp    .-56            ; 0x0 <__vectors>

00000038 <main>:
  38:   ff cf           rjmp    .-2             ; 0x38 <main>

0000003a <_exit>:
  3a:   f8 94           cli

0000003c <__stop_program>:
  3c:   ff cf           rjmp    .-2             ; 0x3c <__stop_program>

If you follow the sequence of that then at power on control enters at 0: where it RJMP's over the vector table (another C provided resource) to some code that sets the stack and makes sure R1 and SREG hold 0 but then there's a whole new block of code called _do_copy_data() that's been inserted. C "knows" that as soon as I add some initialised data to a C program that because such data is expected to be writeable it has to be in RAM and yet at power on it has to start by holding a copy of the initial values in flash. So it needs a loop to read from flash (LPM) and store (ST) the values in RAM which is exactly what you see at the core of the code it has provided:

  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0

So in theory you need something similar.

 

HOWEVER you are writing in a mixed C+Asm environment so you might as well let C do this for you! If you just add a C file to your project that contains nothing but:

uint8_t Table2[] = {
	10,   21,     32,     43,     54,     65,    76,    87,  
	98,   109,    110,    121,    132,    143,   154,   165,
	176,  187,    198,    209,    216,    221,   232,   243,
	1,    2,      3,      4,      5,      6,     7,     8
};

then when compiled that generates this Asm source:

C:\SysGCC\avr\bin>avr-gcc -c -mmcu=attiny13 -Os avr.c -o avr.o -save-temps

C:\SysGCC\avr\bin>type avr.s
        .file   "avr.c"
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .section        .text.startup,"ax",@progbits
.global main
        .type   main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
.L2:
        rjmp .L2
        .size   main, .-main
.global Table2
        .data
        .type   Table2, @object
        .size   Table2, 32
Table2:
        .byte   10
        .byte   21
        .byte   32
        .byte   43
        .byte   54
        .byte   65
        .byte   76
        .byte   87
        .byte   98
        .byte   109
        .byte   110
        .byte   121
        .byte   -124
        .byte   -113
        .byte   -102
        .byte   -91
        .byte   -80
        .byte   -69
        .byte   -58
        .byte   -47
        .byte   -40
        .byte   -35
        .byte   -24
        .byte   -13
        .byte   1
        .byte   2
        .byte   3
        .byte   4
        .byte   5
        .byte   6
        .byte   7
        .byte   8
        .ident  "GCC: (GNU) 5.3.0"
.global __do_copy_data

Note the very last line of that. By simply referring to _do_copy_data as ".global" this code ensures that at link time the code of _do_copy_data will be inserted into your program. So I can do something like:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t Table2[] = {
        10,   21,     32,     43,     54,     65,    76,    87,
        98,   109,    110,    121,    132,    143,   154,   165,
        176,  187,    198,    209,    216,    221,   232,   243,
        1,    2,      3,      4,      5,      6,     7,     8
};

C:\SysGCC\avr\bin>type asm.S
#define __SFR_OFFSET 0
#include <avr/io.h>

        .extern Table2
        .global main
main:
        LDI r24, 0xFF
        OUT     DDRB, r24
        RCALL init_vars
loop:
        LD r24, X
        INC r24
        ST X+, r24
        DEC r25
        BRNE loop
        RCALL init_vars
        RJMP loop

init_vars:
        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)
        LDI r25, 24 ; bytes in array
        RET


C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c asm.S -o avr.elf -save-temps

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   19 c0           rjmp    .+50            ; 0x36 <__bad_interrupt>
   4:   18 c0           rjmp    .+48            ; 0x36 <__bad_interrupt>
   6:   17 c0           rjmp    .+46            ; 0x36 <__bad_interrupt>
   8:   16 c0           rjmp    .+44            ; 0x36 <__bad_interrupt>
   a:   15 c0           rjmp    .+42            ; 0x36 <__bad_interrupt>
   c:   14 c0           rjmp    .+40            ; 0x36 <__bad_interrupt>
   e:   13 c0           rjmp    .+38            ; 0x36 <__bad_interrupt>
  10:   12 c0           rjmp    .+36            ; 0x36 <__bad_interrupt>
  12:   11 c0           rjmp    .+34            ; 0x36 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_copy_data>:
  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   e8 e5           ldi     r30, 0x58       ; 88
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>
  32:   02 d0           rcall   .+4             ; 0x38 <main>
  34:   0f c0           rjmp    .+30            ; 0x54 <_exit>

00000036 <__bad_interrupt>:
  36:   e4 cf           rjmp    .-56            ; 0x0 <__vectors>

00000038 <main>:
  38:   8f ef           ldi     r24, 0xFF       ; 255
  3a:   87 bb           out     0x17, r24       ; 23
  3c:   07 d0           rcall   .+14            ; 0x4c <init_vars>

0000003e <loop>:
  3e:   8c 91           ld      r24, X
  40:   83 95           inc     r24
  42:   8d 93           st      X+, r24
  44:   9a 95           dec     r25
  46:   d9 f7           brne    .-10            ; 0x3e <loop>
  48:   01 d0           rcall   .+2             ; 0x4c <init_vars>
  4a:   f9 cf           rjmp    .-14            ; 0x3e <loop>

0000004c <init_vars>:
  4c:   a0 e6           ldi     r26, 0x60       ; 96
  4e:   b0 e0           ldi     r27, 0x00       ; 0
  50:   98 e1           ldi     r25, 0x18       ; 24
  52:   08 95           ret

00000054 <_exit>:
  54:   f8 94           cli

00000056 <__stop_program>:
  56:   ff cf           rjmp    .-2             ; 0x56 <__stop_program>

So the code here has the _do_copy_data loop. You can see from:

  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   e8 e5           ldi     r30, 0x58       ; 88
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>

that it is going to copy bytes from flash address 0x0058 onwards and write the data to 0x0060 onwards in RAM.

 

Later on the .S code I wrote refers to:

        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)

and you can see this is actually implemented as:

0000004c <init_vars>:
  4c:   a0 e6           ldi     r26, 0x60       ; 96
  4e:   b0 e0           ldi     r27, 0x00       ; 0

so it's looking at that same 0x0060 address in RAM where the copied data is now located.

 

Bottom line: don't write your own do_copy_data() if you can use the one that the C compiler provides anyway.

 

BTW there's also this:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t buffer[20];
int n;
long l;

int main(void) {
        while(1);
}
C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c -o avr.elf -save-temps

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   16 c0           rjmp    .+44            ; 0x30 <__bad_interrupt>
   4:   15 c0           rjmp    .+42            ; 0x30 <__bad_interrupt>
   6:   14 c0           rjmp    .+40            ; 0x30 <__bad_interrupt>
   8:   13 c0           rjmp    .+38            ; 0x30 <__bad_interrupt>
   a:   12 c0           rjmp    .+36            ; 0x30 <__bad_interrupt>
   c:   11 c0           rjmp    .+34            ; 0x30 <__bad_interrupt>
   e:   10 c0           rjmp    .+32            ; 0x30 <__bad_interrupt>
  10:   0f c0           rjmp    .+30            ; 0x30 <__bad_interrupt>
  12:   0e c0           rjmp    .+28            ; 0x30 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_clear_bss>:
  1c:   20 e0           ldi     r18, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   01 c0           rjmp    .+2             ; 0x26 <.do_clear_bss_start>

00000024 <.do_clear_bss_loop>:
  24:   1d 92           st      X+, r1

00000026 <.do_clear_bss_start>:
  26:   aa 37           cpi     r26, 0x7A       ; 122
  28:   b2 07           cpc     r27, r18
  2a:   e1 f7           brne    .-8             ; 0x24 <.do_clear_bss_loop>
  2c:   02 d0           rcall   .+4             ; 0x32 <main>
  2e:   02 c0           rjmp    .+4             ; 0x34 <_exit>

00000030 <__bad_interrupt>:
  30:   e7 cf           rjmp    .-50            ; 0x0 <__vectors>

00000032 <main>:
  32:   ff cf           rjmp    .-2             ; 0x32 <main>

00000034 <_exit>:
  34:   f8 94           cli

00000036 <__stop_program>:
  36:   ff cf           rjmp    .-2             ; 0x36 <__stop_program>

This time the C code has some variables but they are not being given initial values:

uint8_t buffer[20];
int n;
long l;

That is simply 26 bytes of variables and because no initial value is given C promises to set all the bytes to 0. You can see it doing that here:

0000001c <__do_clear_bss>:
  1c:   20 e0           ldi     r18, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   01 c0           rjmp    .+2             ; 0x26 <.do_clear_bss_start>

00000024 <.do_clear_bss_loop>:
  24:   1d 92           st      X+, r1

00000026 <.do_clear_bss_start>:
  26:   aa 37           cpi     r26, 0x7A       ; 122
  28:   b2 07           cpc     r27, r18
  2a:   e1 f7           brne    .-8             ; 0x24 <.do_clear_bss_loop>

That starts at 0x0060 once again (the start of RAM in this tiny13) and it uses the fact that the C compiler always tries to hold 0x00 in R1 so it writes that value to every location from 0x0060 to 0x007A - that is the 26 bytes that form "buffer", "n" and "l".

 

Again, your .S code can use this too. If you want blocks of RAM guaranteed to hold 0x00 then simply use a .c file to set up the "variables". As you saw before the link between C and Asm works both ways. Not only can you have .global in a .S file that can be called from .c but you can create objects in .c files (that will be .global by default) and then just .extern them in your own .S code and refer to them by name as I did with:

        .extern Table2

...

init_vars:
        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want to remember the table changes after reboot place it in EEPROM, and if changes are rare keep them there, else copy to RAM

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greg_Muth  :

 

Thank you for the (now so obvious!) mistake.  I am a beginner... I am learning form all of this. Thank you for being patient with me!

 

I'm getting there none the less!  :-)

 

So... I can read write to say 0x0103  but 0x0104 blows up my little system...  So apparently there is "stuff" in 0x0100 and higher already. I just don't know exactly where : 

 

My question now is how can i find the dog poop in the yard to avoid it?   Like the I/O registers it appears not all of 0x0100 to 0x08FF is wide open country.

 

Thank you again!!

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
I can read write to say 0x0103 but 0x0104 blows up my little system
Show your code. If you are writing asm (and not using any C/C++) then all of the SRAM is yours to do with as you like...

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
So... I can read write to say 0x0103 but 0x0104 blows up my little system... So apparently there is "stuff" in 0x0100 and higher already. I just don't know exactly where : My question now is how can i find the dog poop in the yard to avoid it? Like the I/O registers it appears not all of 0x0100 to 0x08FF is wide open country.
In one of your threads I posted a picture from the 328P datasheet. In this thread Greg posted the same thing again (which is what happens when questions are cross posted). In case you missed it here it is again:

 

 

That is the definitive layout of everything in the "RAM" space of your 328P AVR.

 

What it's telling you is that RAM addresses 0x0000..0x001F (32 bytes) are a copy of the 32 machine registers in the AVR CPU core. From 0x0020 to 0x5F are the lower SFR (Special Function Registers). These are the control registers for some of the peripherals in your AVR. There's actually more than just the 64 bytes from 0x0020 to 0x005F but those ones are a "special case" because they can be accessed more quickly and compactly using IN/OUT if you want. Everything else in this RAM "map" requires you to use LDS/LD/STS/ST. So the block of SFR actually continues on beyond 0x005F. The diagram tells you that the next chunk (that can not be accessed using IN/OUT) are from 0x0060 to 0x00FF. That's another 160 bytes. So in total there are 64 + 160 = 224 bytes set aside for the peripherals. Finally true RAM starts at 0x100. There is 2K (0x800 bytes) of it so it stretches between 0x100 and 0x8FF.

 

It is in that latter section you are free to put what you like. 0x103 and 0x104 are within that region. You said:

AVR_Beginner wrote:
but 0x0104 blows up my little system...

That is a complete and utter mystery. As long as your code is the only software in the system you have complete control of every byte from 0x0100 to 0x8FF and a write to 0x104 should have no effect at all. Could it be that there is some confusion in your code and you have accidentally used location 0x0104 for two different things?

 

That is the kind of mistake a C compiler cannot make which is why I really recommend you consider what I told you in #16 above. If you let C and the linker position all your "counts" and "Table2"s and whatever else you want to call your storage locations in RAM then there's no chance of inadvertent overlaps as the linker's primary job is to lay things out nicely so everything fits together with no overlaps (and no gaps).

 

Actually the same is true of Asm. If you write:
 

.data
.global foo
foo:
  .byte 57
bar:
  .byte 123

then the linker will pick locations for these for you. It will likely (in a 328) with no other variables put "foo" at 0x100 an "bar" at 0x101.

 

So don't hard code things in your code like:

lds r23, 0x103

but stick to things like:

lds r23, bar

and the actual location of "bar" will be filled in later when the linker has decided where it is being positioned.

Last Edited: Tue. Dec 5, 2017 - 01:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
To me it seems the very end of it (of course, why not? LOL) is relevant to my question: the part with the opcodes.
AVR_Beginner wrote:
learn AVR ASM.

While having a datasheet handy for study. if you want to learn/do ASM you need the AVR Instruction Set document.  Those 200 pages are more important than the one-page summary in the datasheet.  A link from Atmel's Web site (which may go obsolete soon) http://www.atmel.com/images/Atme...

 

While dated, I'd suggest a trip to www.avrbeginners.net  Good discussion and practical worked examples.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
but 0x0104 blows up my little system...

As with the other respondents, tell more about that.  When your Uno blows up, do you then need to get another one?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Everyone thank you again!

 

 

Another beginners mistake:  I pulled up a memory location 0x0104 that I had not yet written to... thus had who knows what and... my out put was gibberish.

 

Thank you ALL for being patient with me as I cut my teeth.

 

theusch: thank you for the link... that is exactly what I was trying to find: all the opcodes and where / when / how to use them!  Gonna print out that "phone book" today :-)

 

Again... thank you every one!

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have in mind that the instruction list show all AVR's instructions, with no note if your AVR has it!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson:

 

 

Thank you: I printed out that memory map Sunday and it is pinned to the wall above my computer. I was scratching my head (a lot!) earlier with my crazy 0x0104 memory issue. I now realize my mistake all along!  Thank you all again!

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:

AVR_Beginner wrote:
I can read write to say 0x0103 but 0x0104 blows up my little system
Show your code. If you are writing asm (and not using any C/C++) then all of the SRAM is yours to do with as you like...

 

 

Keep in mind that as his code grows, he will need to be aware of where the stack is and how much stack memory he uses, vs. what he uses for his program.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Cliff:

 

Yup!  I see it clearly now.  I am going to go back to my old .elf files (Arduino erases them un-recoverably but I have started to take them before erasure to study).  Thanks for the look -see and i am still reading through the rest if your post.  Thank you again!!

 

 

 

clawson wrote:

As others have noted C does a "clever trick" that you may not be aware of. If you write:

#include <avr/io.h>

int main(void) {
	while(1);
}

then when you build that (I picked a small AVR so few interrupt vectors):

C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   0e c0           rjmp    .+28            ; 0x20 <__bad_interrupt>
   4:   0d c0           rjmp    .+26            ; 0x20 <__bad_interrupt>
   6:   0c c0           rjmp    .+24            ; 0x20 <__bad_interrupt>
   8:   0b c0           rjmp    .+22            ; 0x20 <__bad_interrupt>
   a:   0a c0           rjmp    .+20            ; 0x20 <__bad_interrupt>
   c:   09 c0           rjmp    .+18            ; 0x20 <__bad_interrupt>
   e:   08 c0           rjmp    .+16            ; 0x20 <__bad_interrupt>
  10:   07 c0           rjmp    .+14            ; 0x20 <__bad_interrupt>
  12:   06 c0           rjmp    .+12            ; 0x20 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61
  1c:   02 d0           rcall   .+4             ; 0x22 <main>
  1e:   02 c0           rjmp    .+4             ; 0x24 <_exit>

00000020 <__bad_interrupt>:
  20:   ef cf           rjmp    .-34            ; 0x0 <__vectors>

00000022 <main>:
  22:   ff cf           rjmp    .-2             ; 0x22 <main>

00000024 <_exit>:
  24:   f8 94           cli

00000026 <__stop_program>:
  26:   ff cf           rjmp    .-2             ; 0x26 <__stop_program>

So that is what an "almost empty" C program looks like. But look what happens if I add your array to that:

#include <avr/io.h>

uint8_t Table2[] = {
	10,   21,     32,     43,     54,     65,    76,    87,
	98,   109,    110,    121,    132,    143,   154,   165,
	176,  187,    198,    209,    216,    221,   232,   243,
	1,    2,      3,      4,      5,      6,     7,     8
};

int main(void) {
	while(1);
}

now when I build I get:

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   19 c0           rjmp    .+50            ; 0x36 <__bad_interrupt>
   4:   18 c0           rjmp    .+48            ; 0x36 <__bad_interrupt>
   6:   17 c0           rjmp    .+46            ; 0x36 <__bad_interrupt>
   8:   16 c0           rjmp    .+44            ; 0x36 <__bad_interrupt>
   a:   15 c0           rjmp    .+42            ; 0x36 <__bad_interrupt>
   c:   14 c0           rjmp    .+40            ; 0x36 <__bad_interrupt>
   e:   13 c0           rjmp    .+38            ; 0x36 <__bad_interrupt>
  10:   12 c0           rjmp    .+36            ; 0x36 <__bad_interrupt>
  12:   11 c0           rjmp    .+34            ; 0x36 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_copy_data>:
  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   ee e3           ldi     r30, 0x3E       ; 62
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>
  32:   02 d0           rcall   .+4             ; 0x38 <main>
  34:   02 c0           rjmp    .+4             ; 0x3a <_exit>

00000036 <__bad_interrupt>:
  36:   e4 cf           rjmp    .-56            ; 0x0 <__vectors>

00000038 <main>:
  38:   ff cf           rjmp    .-2             ; 0x38 <main>

0000003a <_exit>:
  3a:   f8 94           cli

0000003c <__stop_program>:
  3c:   ff cf           rjmp    .-2             ; 0x3c <__stop_program>

If you follow the sequence of that then at power on control enters at 0: where it RJMP's over the vector table (another C provided resource) to some code that sets the stack and makes sure R1 and SREG hold 0 but then there's a whole new block of code called _do_copy_data() that's been inserted. C "knows" that as soon as I add some initialised data to a C program that because such data is expected to be writeable it has to be in RAM and yet at power on it has to start by holding a copy of the initial values in flash. So it needs a loop to read from flash (LPM) and store (ST) the values in RAM which is exactly what you see at the core of the code it has provided:

  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0

So in theory you need something similar.

 

HOWEVER you are writing in a mixed C+Asm environment so you might as well let C do this for you! If you just add a C file to your project that contains nothing but:

uint8_t Table2[] = {
	10,   21,     32,     43,     54,     65,    76,    87,
	98,   109,    110,    121,    132,    143,   154,   165,
	176,  187,    198,    209,    216,    221,   232,   243,
	1,    2,      3,      4,      5,      6,     7,     8
};

then when compiled that generates this Asm source:

C:\SysGCC\avr\bin>avr-gcc -c -mmcu=attiny13 -Os avr.c -o avr.o -save-temps

C:\SysGCC\avr\bin>type avr.s
        .file   "avr.c"
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .section        .text.startup,"ax",@progbits
.global main
        .type   main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
.L2:
        rjmp .L2
        .size   main, .-main
.global Table2
        .data
        .type   Table2, @object
        .size   Table2, 32
Table2:
        .byte   10
        .byte   21
        .byte   32
        .byte   43
        .byte   54
        .byte   65
        .byte   76
        .byte   87
        .byte   98
        .byte   109
        .byte   110
        .byte   121
        .byte   -124
        .byte   -113
        .byte   -102
        .byte   -91
        .byte   -80
        .byte   -69
        .byte   -58
        .byte   -47
        .byte   -40
        .byte   -35
        .byte   -24
        .byte   -13
        .byte   1
        .byte   2
        .byte   3
        .byte   4
        .byte   5
        .byte   6
        .byte   7
        .byte   8
        .ident  "GCC: (GNU) 5.3.0"
.global __do_copy_data

Note the very last line of that. By simply referring to _do_copy_data as ".global" this code ensures that at link time the code of _do_copy_data will be inserted into your program. So I can do something like:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t Table2[] = {
        10,   21,     32,     43,     54,     65,    76,    87,
        98,   109,    110,    121,    132,    143,   154,   165,
        176,  187,    198,    209,    216,    221,   232,   243,
        1,    2,      3,      4,      5,      6,     7,     8
};

C:\SysGCC\avr\bin>type asm.S
#define __SFR_OFFSET 0
#include <avr/io.h>

        .extern Table2
        .global main
main:
        LDI r24, 0xFF
        OUT     DDRB, r24
        RCALL init_vars
loop:
        LD r24, X
        INC r24
        ST X+, r24
        DEC r25
        BRNE loop
        RCALL init_vars
        RJMP loop

init_vars:
        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)
        LDI r25, 24 ; bytes in array
        RET

C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c asm.S -o avr.elf -save-temps

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   19 c0           rjmp    .+50            ; 0x36 <__bad_interrupt>
   4:   18 c0           rjmp    .+48            ; 0x36 <__bad_interrupt>
   6:   17 c0           rjmp    .+46            ; 0x36 <__bad_interrupt>
   8:   16 c0           rjmp    .+44            ; 0x36 <__bad_interrupt>
   a:   15 c0           rjmp    .+42            ; 0x36 <__bad_interrupt>
   c:   14 c0           rjmp    .+40            ; 0x36 <__bad_interrupt>
   e:   13 c0           rjmp    .+38            ; 0x36 <__bad_interrupt>
  10:   12 c0           rjmp    .+36            ; 0x36 <__bad_interrupt>
  12:   11 c0           rjmp    .+34            ; 0x36 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_copy_data>:
  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   e8 e5           ldi     r30, 0x58       ; 88
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>
  32:   02 d0           rcall   .+4             ; 0x38 <main>
  34:   0f c0           rjmp    .+30            ; 0x54 <_exit>

00000036 <__bad_interrupt>:
  36:   e4 cf           rjmp    .-56            ; 0x0 <__vectors>

00000038 <main>:
  38:   8f ef           ldi     r24, 0xFF       ; 255
  3a:   87 bb           out     0x17, r24       ; 23
  3c:   07 d0           rcall   .+14            ; 0x4c <init_vars>

0000003e <loop>:
  3e:   8c 91           ld      r24, X
  40:   83 95           inc     r24
  42:   8d 93           st      X+, r24
  44:   9a 95           dec     r25
  46:   d9 f7           brne    .-10            ; 0x3e <loop>
  48:   01 d0           rcall   .+2             ; 0x4c <init_vars>
  4a:   f9 cf           rjmp    .-14            ; 0x3e <loop>

0000004c <init_vars>:
  4c:   a0 e6           ldi     r26, 0x60       ; 96
  4e:   b0 e0           ldi     r27, 0x00       ; 0
  50:   98 e1           ldi     r25, 0x18       ; 24
  52:   08 95           ret

00000054 <_exit>:
  54:   f8 94           cli

00000056 <__stop_program>:
  56:   ff cf           rjmp    .-2             ; 0x56 <__stop_program>

So the code here has the _do_copy_data loop. You can see from:

  1c:   10 e0           ldi     r17, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   e8 e5           ldi     r30, 0x58       ; 88
  24:   f0 e0           ldi     r31, 0x00       ; 0
  26:   02 c0           rjmp    .+4             ; 0x2c <__do_copy_data+0x10>
  28:   05 90           lpm     r0, Z+
  2a:   0d 92           st      X+, r0
  2c:   a0 38           cpi     r26, 0x80       ; 128
  2e:   b1 07           cpc     r27, r17
  30:   d9 f7           brne    .-10            ; 0x28 <__do_copy_data+0xc>

that it is going to copy bytes from flash address 0x0058 onwards and write the data to 0x0060 onwards in RAM.

 

Later on the .S code I wrote refers to:

        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)

and you can see this is actually implemented as:

0000004c <init_vars>:
  4c:   a0 e6           ldi     r26, 0x60       ; 96
  4e:   b0 e0           ldi     r27, 0x00       ; 0

so it's looking at that same 0x0060 address in RAM where the copied data is now located.

 

Bottom line: don't write your own do_copy_data() if you can use the one that the C compiler provides anyway.

 

BTW there's also this:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t buffer[20];
int n;
long l;

int main(void) {
        while(1);
}
C:\SysGCC\avr\bin>avr-gcc -mmcu=attiny13 -Os avr.c -o avr.elf -save-temps

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__ctors_end>
   2:   16 c0           rjmp    .+44            ; 0x30 <__bad_interrupt>
   4:   15 c0           rjmp    .+42            ; 0x30 <__bad_interrupt>
   6:   14 c0           rjmp    .+40            ; 0x30 <__bad_interrupt>
   8:   13 c0           rjmp    .+38            ; 0x30 <__bad_interrupt>
   a:   12 c0           rjmp    .+36            ; 0x30 <__bad_interrupt>
   c:   11 c0           rjmp    .+34            ; 0x30 <__bad_interrupt>
   e:   10 c0           rjmp    .+32            ; 0x30 <__bad_interrupt>
  10:   0f c0           rjmp    .+30            ; 0x30 <__bad_interrupt>
  12:   0e c0           rjmp    .+28            ; 0x30 <__bad_interrupt>

00000014 <__ctors_end>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61

0000001c <__do_clear_bss>:
  1c:   20 e0           ldi     r18, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   01 c0           rjmp    .+2             ; 0x26 <.do_clear_bss_start>

00000024 <.do_clear_bss_loop>:
  24:   1d 92           st      X+, r1

00000026 <.do_clear_bss_start>:
  26:   aa 37           cpi     r26, 0x7A       ; 122
  28:   b2 07           cpc     r27, r18
  2a:   e1 f7           brne    .-8             ; 0x24 <.do_clear_bss_loop>
  2c:   02 d0           rcall   .+4             ; 0x32 <main>
  2e:   02 c0           rjmp    .+4             ; 0x34 <_exit>

00000030 <__bad_interrupt>:
  30:   e7 cf           rjmp    .-50            ; 0x0 <__vectors>

00000032 <main>:
  32:   ff cf           rjmp    .-2             ; 0x32 <main>

00000034 <_exit>:
  34:   f8 94           cli

00000036 <__stop_program>:
  36:   ff cf           rjmp    .-2             ; 0x36 <__stop_program>

This time the C code has some variables but they are not being given initial values:

uint8_t buffer[20];
int n;
long l;

That is simply 26 bytes of variables and because no initial value is given C promises to set all the bytes to 0. You can see it doing that here:

0000001c <__do_clear_bss>:
  1c:   20 e0           ldi     r18, 0x00       ; 0
  1e:   a0 e6           ldi     r26, 0x60       ; 96
  20:   b0 e0           ldi     r27, 0x00       ; 0
  22:   01 c0           rjmp    .+2             ; 0x26 <.do_clear_bss_start>

00000024 <.do_clear_bss_loop>:
  24:   1d 92           st      X+, r1

00000026 <.do_clear_bss_start>:
  26:   aa 37           cpi     r26, 0x7A       ; 122
  28:   b2 07           cpc     r27, r18
  2a:   e1 f7           brne    .-8             ; 0x24 <.do_clear_bss_loop>

That starts at 0x0060 once again (the start of RAM in this tiny13) and it uses the fact that the C compiler always tries to hold 0x00 in R1 so it writes that value to every location from 0x0060 to 0x007A - that is the 26 bytes that form "buffer", "n" and "l".

 

Again, your .S code can use this too. If you want blocks of RAM guaranteed to hold 0x00 then simply use a .c file to set up the "variables". As you saw before the link between C and Asm works both ways. Not only can you have .global in a .S file that can be called from .c but you can create objects in .c files (that will be .global by default) and then just .extern them in your own .S code and refer to them by name as I did with:

        .extern Table2

...

init_vars:
        LDI r26, lo8(Table2)
        LDI r27, hi8(Table2)

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank Cliff!

 

 

I have also been able to make an ASM "start up" routine that manually loads it into Data RAM.  So now I have more than one way to do it.  The "extern Table2" I knew about before but was using "pure" ASM code as much as I could.   So now I have the ability to load a static / un editable table / array into Flash / Program space and alternately put a table that can be edited into Data Memory.

 

Its clear the C compiler does a lot for me "under the hood".  I choose to avoid it for the time being because

   1) I don't know C  (except for Arduino) and

   2) By using pure ASM as much as I can, I am learning the ins and outs of ASM

 

 

Thank you Cliff and everyone else for being patient with this old dog as he tries to learn new tricks.  :-)

 

 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use the ".dseg" in assembler to reserve data space:

 

.dseg
inbuf:    .BYTE 120            ; input buffer
outbuf:    .BYTE 120            ; output buffer to GPS

;
.cseg

 

 

access like:

 

        ldi YH,HIGH(outbuf)        ;point the Y register at the outbuf
        ldi YL,LOW(outbuf)
        ldi ZH,HIGH(rmconly<<1);get the address of message in program memory
        ldi ZL,LOW(rmconly<<1)
movmsg:
        lpm r16,Z+                ;load from program memory
        st Y+,r16                ;store in data segment, outbuf

        cpi r16,10                ;was the character a NL?
        brne movmsg

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Read:

 

The assembler manual:  http://ww1.microchip.com/downloa...

 

The Instruction Set Manual:  http://ww1.microchip.com/downloa...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to warn you that jstampfl's two recent posts are not relevant to what you are doing. Like most people he assumed you would be using the assembler that most people programming AVRs in assembler would be using (the Atmel one). But you are using the GNU assembler that comes with the C compiler so things like:

.dseg
inbuf:    .BYTE 120            ; input buffer
outbuf:    .BYTE 120            ; output buffer to GPS

are not that simple. As I explained elsewhere, for the GNU linking assembler you use directives such as .byte and .word in some assigned memory section (often .data) and then the linker (not the .dseg location counter from Asm2) decides where that entire section is placed. Usually it's going to be in this scheme:

 

malloc-std.png

 

Note also that ".byte" in Atmel Asm2 and .byte in GNU "as" are quite, quite different. The Atmel version uses:

.byte 120

to mean "allocate 120 bytes at this label" while for GNU as the same directive creates a one byte location containing the value 120 (0x78).

 

The Atmel assembler manual says:

 

The GNU manual says:

 

They are quite different. To do the equivalent of Atmel's .byte in GNU as use one of .skip, .space or .zero. The other way round the Atmel assembler's nearest equivalent of GNU .byte would be .db

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Cliff for clearing that up... I was wondering about that. Even when I want to load a table's location into Z I have to use  ZH , hi( Tab1) and ZL , lo(Tab1) so i know there are semantic differences now :-)

 

Thank you again!

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
when I want to load a table's location into Z I have to use  ZH , hi( Tab1) and ZL , lo(Tab1)

The force is getting stronger with you, young Padawan! (-:

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
The force is getting stronger with you, young Padawan!

 

Soon it will be "When I left you I was but a learner, now I am the master!"   LOL

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

then simply use a .c file to set up the "variables".

This is a excellent idea, and I don't think Cliff emphasized it enough.

If you create "sketch.ino" that contains nothing but variables:

int a;
int b = 3;
float pi = 3.14;
char myarray[] = "this is a test";
char buffer[20];

Then all of those variables will be available to any .S file as symbols, they'll be put in the appropriate sections of RAM, and the C startup code will initialize them for you.

You don't even need the ".extern" declarations in the .S file, since the gnu assembler defaults to treating any undefined symbols as external.

 

It'll also be an abject lesson in how assembly language symbols are "un-typed."  Your assembly code would be able to do something like:

    lds r16, pi   ;; get pi
    subi r16, 1  ;; subtract 1

Even though there is very little chance that does anything meaningful...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw:  Yes, I was already doing that a while ago.  My goal is pure ASM so I am trying to avoid any C or Arduino code.

 

Basically my Arduino file is a .ino shell and the real "work" is done in the .S file.  This forces me to learn ASM in totality.  That will be more involved as i do manually what C could do for me automatically, but that is the point: to learn.

 

Thank you for the tip none the less.  I am trying to avoid any C / Arduino code as much as possible.

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So now I'm curious.  Is there a standard method that hardcore assembly language programmers use for putting initialized data in RAM on ROM-based microcontrollers?  (I guess all of my "hardcore assembly" was on run-from-RAM CPUs with an operating system that loaded the initial contents from a disk...)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Is there a standard method that hardcore assembly language programmers use for putting initialized data in RAM on ROM-based microcontrollers? 

 

I dunno... What I am doing is trying to learn ASM.  It sounds like using the GNU C-ASM flexibility is a preferred way in many / most cases for many timegs. But by eliminating that as an option I force myself to learn all of the ins and outs of ASM  (allbeit with the caveot that the assembler I use is not the Atmel one, but I will still learn all the principals, registers, timers, etc etc).

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Is there a standard method that hardcore assembly language programmers use for putting initialized data in RAM on ROM-based microcontrollers? 

 

By analogy:  If you drive a stick and get to know how to use that you can later learn and enjoy automatic.  But, if all you drive is a automatic car you will not know how to drive stick and get the feel for engine braking, when to shift, how low gear helps in snow, how keeping in high gear saves on gas, etc, etc

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What I am doing is trying to learn ASM. 

 I understand, and I support this in principle.  But the whole "initialized data" thing is a sort-of meta-issue that you wouldn't run into if you were to (for example) learn x86 assembly language by writing programs for a windows PC, or Z80 code on a CP/M system.    Now, part of the meta-issue is that you can write a lot of programs that don't use any initialized RAM data at all; an assembly language AVR program is more likely to put strings and things in flash by default (while in C that requires special effort.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
So now I'm curious. Is there a standard method that hardcore assembly language programmers use for putting initialized data in RAM on ROM-based microcontrollers?
I think you'll find that any "serious" Asm program for AVR just ends up reimplementing that same kind of "LPM;ST" loop that the C compiler will provide.

 

Of course "real programmers" don't rely on "library code" and if they want printf() or strcpy() or even _do_clear_bss() or _do_copy_data() they aren't going to let no sissy C compiler supply those things when they can just retype the same code  every time!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 retype the same code  every time

Now I understand why C people think it take so long to write ASM!

 

No there is no general way, other than have a good structure for your project, and have a good plan of how the HW will be used.

Have in mind (find out of) what will be the main problem there is to be solved (speed (and which parts), size (both flash and RAM), HW timers etc).

Often part of the code have to run relative fast around ISR(s), and then have a slow "main".

 

And an important thing is always to have a plan for how you will test the code before you write it.

 

 

   

 

 

   

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1)  Re "hard core" ASM programming on an operating system -- you mean that your "hard core" assembly language had magic incantations similar to the C prologue to set initial values?!?  How "hard code" is that?  Why don't I remember that from my ASM86/MASM/TASM days?

 

2)  Am I the only one that has very few if any initial values for C variables in AVR apps?  The thinking is that these are MICROcontrollers with limited resources.  So if nothing else, the flash copy takes space.

 

2a)  Hmmm--my method of jiggering values so 0 is the starting value still depends on the BSS (or equivalent for your toolchain) clear. 

2b)  Did the "hard code" ASM programming assume [S]RAM in a certain state?

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ou mean that your "hard core" assembly language had magic incantations similar to the C prologue to set initial values?!?  How "hard code" is that?  Why don't I remember that from my ASM86/MASM/TASM days?

The "incantations" were the same as you have in AVR assembler, ".byte", or "dw" or similar.  What you DIDN'T have was the need to say "this label is in this memory section."  Since the whole program would be loaded into RAM, you could intermix your code and data with no particular attention paid to where they ended up wrt each other.

For example, I have this x86 decimal output func (MASM, I think):

public decout
ten	dw	10
decout	proc near
	xor	dx,dx
	div	ten
	push	dx
	or	ax,ax
	jz	DECOU2
	call	decout
decou2:	pop	ax
	add	al,'0'
	call	typchr
	ret
decout	endp

Where "10" just appears immediately before the code that uses it (and would be writable, once the program is loaded into RAM.)

Similarly there's some 8080 Forth code:

DP0	.DB	83H	; LIT
	.DB	"LI"
	.DB	'T'+80H
	.DW	0	; (LFA)=0 MARKS END OF DICTIONARY
LIT	.DW	$+2	;(S1) <- ((IP))
	LDAX	B	; (HL) <- ((IP)) = LITERAL
	INX	B	; (IP) <- (IP) + 2
	MOV	L,A	; LB

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I use AVRASM2 for assembler work on the AVR.  Here is an example of the framework that I use for most assembler programs.  Start with a description, then include the directives file for the AVR device.  Next are the constants and register names.  Then there is a declared DATA section for variables.  This is where your table needs to be if you are going to write values to this table.  If this table is going to have preset values when the processor is turned on, then the RESET code that configures the system before any other code is run must move these preset values from storage in the AVR's program memory section to the data memory section.  The LD and ST instructions work with the data memory section: LPM reads from the program memory section.

;  ****************************************************************************************************
;  * P2Km168.asm  -  PC keyboard to MIDI converter for E-Mu Proteus 2000 tone module series
;  * version 0.1.5  2/13/2012  change from Mega48 to Mega168 AVR
;  * version 0.1.4  3/11/2011
;  * Use a equally-proportioned font like Terminal or Lucida Console to align correctly.  Tabs = 8.
;  *
;  * Code designed for the Atmel AVR Mega168 [available at www.digikey.com]
;  * Mega168 fuses:  extended:0xff high:0xD7 low:0xE2  -  one RC calibration byte
;  * extended high byte  low byte
; -  1: 7  - RSTDISBL no 1: 7  - CKDIV8  off
; -  1: 6  - DWEN no  1: 6  - CKOUT   off
; -  0: 5  - SelfProgEn on 1: 5  - SUT1  slow rise
; -  1: 4  - WDTon? no 0: 4  - SUT0
; -  0: 3  - EESAVE yes 0: 3  - CLKsel3  0010=8MHz
; -  1: 2  - BODlev2  0: 2  - CLKsel2  internal RC
; -  1: 1  - BODlev1  1: 1  - CLKsel1
; 1:Self-prg en   1: 0  - BODlev0  0: 0  - CLKsel0
;
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

.nolist
.include "m168def.inc"
.list

; constants
.equ clock   = 8000000 ; 8 MegaHertz resistor-capacitor oscillator inside the microcontroller
.equ MIDIbaud  = (clock/ (31250 * 16)) - 1
.equ KBPort    = PinD  ; KBport,KBdataPin


                                                                                                                           
;*********************************************************************************************                             
;  Register definitions                                                                                                    
;                                                                                                                          
;  lower registers                                                                                                         
; r0 is used for EEPROM addressing                                                                                         
                                                                                                                           
.def SREGtemp = r3 ; storage of AVR status register flags during an interrupt                                                                                                                       
.def ZeroReg  = r15                                                                                                  
                                                                                                                         
;  upper registers                                                                                                         
.def temp  = r16 ; scratch register                                                                             
.def temp2  = r17 ; temporary register                                                                                                                                                                      
.def irqtemp  = r18 ; interrupt's scratch register                                                                 
                               
.def keyflags = r19  ; application program flags for decoding the latest byte from PS2 keyboard.                    
.equ   Caps_flag = 6 ; Caps Lock pressed on -- no NoteOff transmitted                                                   
.equ   Alt_flag = 5 ; left Alt key is being pressed                                                                    
.equ   Break_flag = 4 ; the last scancode was an 0xf0 - the break code sent when a key is released                   
.equ   PressRel_flag = 3 ; set means turn on the note whose MIDI number was decoded from the most recent scancode       
.equ   NewScanCode = 2 ; a byte has been received from the keyboard                                                   
.equ   Extended_flag = 1 ; the extended scancode 0xE0 was sent - use the E0table to decode scancode                     
.equ   Sustain_Flag  = 0                                                                                                
                                                                                                                           
.def ProgramFlags = r20 ; MIDI soft-UART control register. Bits 3-0 are the shift register counter.                
.equ   DoADCnow = 6 ; signals main program that a 1/20 second interval has passed since last ADC readings              
.equ   AuditionOn = 5                                                                                                
      

;******************************************************
;
;  Data Segment  1K bytes  0x100 - 0x4ff
;
;  Static RAM Arrays, tables, stacks, and queues
;
;******************************************************
.dseg
TxQueue: .byte TXQSIZE ; 64 bytes 0x100 - 0x13f  PutMIDIbyte
RxQueue: .byte RXQSIZE ; 64 bytes 0x140 - 0x17f
; all MIDI note values being currently played are stored here; 8 notes can play at one time
SoundingNotes: .byte 8 ; 0x180 - 0x187
CurrentVoice: .byte 1 ; 0x188   selected voice on synth - used for incrementing and decrementing voice
CurrentBank: .byte 1 ; 0x189
bitcount: .byte 1 ; 0x18a   INT0 interrupt routine on PB2 - getscan IRQ routine     



;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.CSEG ; 16K Flash address range  0x000 - 0xfff
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.org 000   ; Flash memory 2 bytes per instruction
 jmp reset  ;  0x0000 Power_On/Reset vector
;; interrupt vectors for Mega168, vectors are two instructions in size
 jmp GetScan_IRQ ;= 0x0002 External Interrupt Request 0
 reti nop  ;= 0x0004 External Interrupt Request 1
 reti nop  ;= 0x0006 Pin Change Interrupt Request 0
 reti nop  ;= 0x0008 Pin Change Interrupt Request 0
 reti nop  ;= 0x000a Pin Change Interrupt Request 1
 reti nop  ;= 0x000c Watchdog Time-out Interrupt
 reti nop  ;= 0x000e Timer/Counter2 Compare Match A
 reti nop  ;= 0x0010 Timer/Counter2 Compare Match A
 reti nop  ;= 0x0012 Timer/Counter2 Overflow
 reti nop  ;= 0x0014 Timer/Counter1 Capture Event
 reti nop  ;= 0x0016 Timer/Counter1 Compare Match A
 reti nop  ;= 0x0018 Timer/Counter1 Compare Match B
 jmp T1OVF_IRQ ;= 0x001a Timer/Counter1 Overflow
 reti nop  ;= 0x001c TimerCounter0 Compare Match A
 reti nop  ;= 0x001e TimerCounter0 Compare Match B
 reti nop  ;= 0x0020 Timer/Couner0 Overflow
 reti nop  ;= 0x0022 SPI Serial Transfer Complete
 jmp RxC_IRQ  ;= 0x0024 USART Rx Complete
 jmp UDRE_IRQ ;= 0x0026 USART, Data Register Empty
 reti nop  ;= 0x0028 USART Tx Complete
 reti nop  ;= 0x002a ADC Conversion Complete
 reti nop  ;= 0x002c EEPROM Ready
 reti nop  ;= 0x002e Analog Comparator
 reti nop  ;= 0x0030 Two-wire Serial Interface
 reti nop  ;= 0x0032 Store Program Memory Read


;*****************************************************************************
;   RESET and Main Code       stack pointer defaults to RAMEND on Mega168
;*****************************************************************************
reset:
; adjust the internal RC system clock so each MIDI bit is exactly 32 microseconds
 ldi temp, low  (EEPROMEND)  ; address where the OSCCAL value for precision MIDI is located
 out EEARL, temp
 sbi EECR, EERE  ; strobe to start the read of the EEPROM data from the address in EEAR
 in  temp, EEDR