Simple memory question

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

I'm very sorry to waste your time, but I just can't seem to figure this out. I'd like to define two constants and one empty space with labels in memory (RAM), then copy the two constants into two registers, add them, and write the result to the reserved space. Thus far I have conceived the code:

.include "m169def.inc"

.org 0x0000
    rjmp RESET

RESET:

.dseg
VarX:	.DB 	6
VarY:	.DB		2
VarZ:	.BYTE	1

.cseg
		LD		r16,VarX
		LD		r17,VarY
		ADD		r16,r17
		ST		VarZ,r16
		RET

But obviously this does not work. It seems as if I can't read directly from the memory by calling a labeled value (variable?). So I'm assuming the memory access method is not by Relative Address Specification but rather Direct Address Specification, and thusly requires a pointer specified in register space holding a memory address to be called. The examples in the AVR documentation say how to use this pointer, but:

                LDI		r26,low(VarX)
		LDI		r27,high(VarX)
		LD		r16,X

does not seem to work.

So, can I copy a value from memory to a register without using a pointer? If not, how can I properly set the pointers in the code above?

Thanks, and sorry if this is in the wrong forum. I looked but couldn't really find similar coding questions in any other forum so I'm assuming this is where to ask, yes?

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

1. You can't have assembly time constants in dseg. Things declared in dseg goes to the SRAM memory which is volatile. In practice that means that DB directives should not go inte the dseg. From the AVR Assembler users guide:

Quote:
The DB directive reserves memory resources in the program memory or the EEPROM memory.

Assembly time constant values can be placed in cseg. Things declared in cseg goes to FLASH. From there You can load them at run time with the lpm instruction.

2. If You want to load a value directly from "data space" (eg. SRAM) then the lds instruction is the one You want.

3. You can load a assembly time constant value in an alternate way by supplying the value in the load instruction itself. Use the ldi instruction.

3. The correct forum would rather be the AVR Forum. (It's no big deal, though!)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Ok, that's a BIG help, thanks! But one more problem has arrisen. When I ammended my code and palced the .DB statements in the .CSEG I get an invalid opcode runtime error. The thing is I can't seem to figure out how I'm deviateing from example given in the AVR documentation. Here's my code for reference:

.include "m169def.inc"

.org 0x0000
    rjmp RESET

RESET:

.DSEG
;VarZ:	.BYTE	1

.CSEG
VarX:	.DB 	6
VarY:	.DB		2
		LDS		r16,VarX
		LDS		r17,VarY
		ADD		r16,r17
;		ST		VarZ,r16
		.EXIT

So, what have I missed?

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

You need to place the constants AFTER your program code. See the .ORG directive in the help files. You should also use it to specify where you are beginning your program too, i.e. where the reset vector jumps to:

.ORG 0x00
rjmp RESET

.ORG 0x2E ; End of the interrupt vectors
RESET:

I like cats, too. Let's exchange recipes.

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

You need to use LPM instead of LDS too

I like cats, too. Let's exchange recipes.

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

This is what you need to do:

.include "m169def.inc" 


.dseg	
.org 	0x100	;byte address in SRAM			
VarX:	.byte	1	

.cseg
.org 0x00 
    rjmp RESET 

.org 0x2E
RESET: 
	LDI		 ZH, 0x02 ;Z pointer is a byte address
	LDI		 ZL, 0x00 ;flash is arranged in words, see instruction set
    LPM      r16, Z+
    LPM      r17, Z 

    ADD      r16,r17 
	
	STS		 VarX, r16

	RJMP     RESET

.org 0x100	;word address in FLASH
Constants:   .DB    0x06, 0x02 

I like cats, too. Let's exchange recipes.

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

ninevoltz9 wrote:
You need to place the constants AFTER your program code.

Well, not really. What You need to do is to see to it that Your data is not executed as instructions. So this could also suffice (just typing this in - not tested):

.org 0x00 
    rjmp RESET

    ; More stuff here. Interrupt vectors etc...

Constants:   .DB    0x06, 0x02 

RESET:
    ; Your code starts here

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Thanks ninevoltz9 And Johan! This has given me a lot to research. But one last question before that: can I give the constants seperate labels and call them directly by the labels? LDS appears to do that, as Johan said, but in order to use it I must somehow store the information in flash beforehand. For example, modifying ninevoltz9's code:

.include "m169def.inc"


.dseg   
.org    0x100   ;byte address in SRAM         
VarX:   .byte   1   

.cseg

.org 0x100   ;word address in FLASH
Constants:   .DB    0x06, 0x02

.org 0x00
    rjmp RESET

.org 0x2E
RESET:
   LDI       ZH, 0x02 ;Z pointer is a byte address
   LDI       ZL, 0x00 ;flash is arranged in words, see instruction set
    LPM      r16, Z+
    LPM      r17, Z

    ADD      r16,r17
   
   STS       VarX, r16

	LDS		r18,VarX	;<<<<HERE

   RJMP     RESET

the STS operation stores the data in the area labeled VarX in memory. Then LDS is capable of reading that value back out of memory into register 18. What I'd like to do is store these constants in flash beforehand, then read them into registers with LDS, then calculate and write back out. I guess I could write them as they are being written now, load them into a register by setting the Z pointer and LPM'ing, shifting Z, LPM'ing them, then writing them back one by one with STS, then once again reading them back from their labels with LDS like this:

.include "m169def.inc"


.dseg   
.org    0x100   ;byte address in SRAM         
VarX:   .byte   1
VarY:	.byte	1
VarZ:	.byte	1   

.cseg

.org 0x100   ;word address in FLASH
Constants:   .DB    0x06, 0x02

.org 0x00
    rjmp RESET

.org 0x2E
RESET:
   LDI       ZH, 0x02 ;Z pointer is a byte address
   LDI       ZL, 0x00 ;flash is arranged in words, see instruction set
    LPM      r18, Z+
    LPM      r19, Z
	STS		VarX,r18
	STS		VarY,r19
	LDS		r16,VarX
	LDS		r17,VarY

    ADD      r16,r17
   
   STS       VarZ, r16	

   RJMP     RESET

but it occrus to me there must be a better way. Is there?

Also, thanks again, you guys are great!

EDIT: Oh, and ninevoltz9, sorry about moving the allocation portions to the head of the file. I come originally from a C background so having them at the top just feels more natural to me. In other assembler varieties I have touched the standard syntax seems to have been to do any allocation after the code segments, but I've never heard anybody argue for it being better or worse for any reason so I've just been putting them at the top. Is there a particular reason you prefer to put them at the bottom? Is it more efficcient that way?

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

Reserve SRAM for data:

.DSEG
.org	0x00C0

ASCII_digs:
	.BYTE 6				; reserve 6 bytes for ASCII digits:
					; offset 0 for sign character (" " or "-")
					; offset 1 for msc (ASCII code, digit 4)
					; offset 2 for character (ASCII code, digit 3)
					; offset 3 for character (ASCII code, digit 2)
					; offset 4 for character (ASCII code, digit 1)
					; offset 5 for lsc (ASCII code, digit 0)

Access data during runtime:

	ldi ZL,low(ASCII_digs)  ; Load array address low
	ldi ZH,high(ASCII_digs) ; Load array address high

	; display "X.XXX"
	ldi	Tmp1,13		; position on display
	ldd	Tmp2,Z+2
	call	Disp_Tx		; write "seconds ones" to display

"Z" (short for ZH:ZL register pair) points to the first (out of six in this example) byte of the data structure, "ldd Tmp2,Z+2" grabs the 3rd byte from the array and stuffs it into Tmp2 for further reference.

Store data to FLASH (code space):

.cseg
.org [whatever]
Baud_Table:	; values for UBRRL (baud rate register)
		; first one placeholder for "UART switched off",
		; last entry to make # of bytes even
.db 0, 191, 95, 47, 31, 23, 15, 11, 7, 5, 3, 0

Access data stored in flash:

        ldi     ZH,high(2*Baud_Table)	; UBRRL values table
        ldi     ZL,low(2*Baud_Table)
	add	ZL,EE_dr	; add offset into table (low byte)
	adc	ZH,Tmp1		; add with carry (high byte)
	lpm	Tmp2,Z		; load predefined UBRRL value
        out     UBRRL,Tmp2
        out     UBRRH,Tmp1

Again, "Z" points to the data (this time we have word addresses because it's flash ROM - the "2*Baud_Table" converts this to the byte address required for lpm), "add/adc" increments the pointer to where I want the access, lpm gets the byte into Tmp1.

the ".org ..." is only needed if you deliberately want to place the data somewhere, normally it's sufficient to place the label and .db's e.g. between to routines or at the end of the actual code - just make sure that there's a ret or reti or rjmp etc. *before* the label - you wouldn't want the AVR trying to decode the data as opcodes ...
HTH

Andreas

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

Thanks for all that information, this should keep me busy for a while!

But my real question is just this: I'd like to allocate space in RAM that I can reference with a label. With LDS you can load something from data space memory, which is exactly what I want to READ with, but I would like to know how to WRITE to data space before the READ.

For example:

Label Command Parameters
VarName 6 ;6 being the number to be initially inserted into the variable

That's it. Like in COMET assembler this would be
VarName DC 6

Or in C the ~equivilent~ idea would be
int VarName = 6;

so, is there a single line/short command in AVR assembler to assign a label useable by LDS in data space memory (with or without initialization, doesn't matter because I can just sts immediately after)?

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

You can not, I repeat not initialize a variable in RAM at compile time.

If, in C, You do

int VarName = 6; 

This will result in
i) allocation in of the space that the variable requires,
ii) Storage of the initialization value in FLASH,
iii) At startup, in the C runtime initialization, the value will be copied from FLASH to RAM (LPM followed by STS).

If You want to initialize a variable in assembler then You will have to do what the compiler does: Store the init value in FLASH, and initialize the variable (with LPM and STS) at startup.

As RAM is volatile there exists no way to magically store an initial value of a variable in it. Every time the program starts the initial value is copied from FLASH to RAM.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Awesome! Thanks Johan, that's exactly what I wanted to know.

And that's basically all I wanted to know about memory for the moment. Thanks!

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

I just realized that I'm participating in two conceptually similar threads on memory and variables. It might be that I had the other thread in memory (pun intended!) when posting to this thread. The other thread is here:https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=191292 and might be interesting to You too!

Edit: Corrected erroneous link above

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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]

Last Edited: Wed. Dec 28, 2005 - 09:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent! And I believe the link you want is something like this: https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=191292.