Computing pointers in gnu as

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

I've spent a couple hours trying to complete an LED-muxing ISR in gnu assembler, and the "hi8/lo8" operators are giving me no end of pain (as usual). The context is: R30 (ZL) has got the zero-based index of the digit to be displayed, and R31 (ZH) is loaded with 0x10, having been used to manipulate some other hardware. From this state, I want to get the Z register pair pointing to Leds

. What I feel I *ought* to be able to do is

    subi r30, -lo8(Leds)
    sbci r31, 16 - hi8(Leds)

An exasperated aside: Of what possible benefit is deciding to offer only a "subtract immediate" operation instead of an "add immediate" one? So some designer gets more opportunities to smugly point out that the effect of adding a constant can be gotten by just subtracting the negative of that constant???

Attempting to compile this in AStudio 6.1 gives an error message for each of the above lines, with the helpful description, "garbage at end of line"

So now begins the guessing game. The gnu manual pages provide this one-sentence description of the "lo8" operator:

gnu as wrote:
This modifier allows you to use bits 0 through 7 of an address expression as 8 bit relocatable expression

, but the definition doesn't do much for me beyond making me curious as to what the distinction between "address expressions" and "relocatable expressions" might be (the manual doesn't define these terms, even though there seems to be ample blank space in the top-level "Expressions" section).

Another round of desperate guessing gives:

#define ALPHA (2 << PORT_OPC_gp)
        ldi r31, ALPHA
        sts PORTC_PIN0CTRL, r31

.set negLeds,(-Leds)
.set highHack, (0xf000 + Leds)
.set highHak2, (-highHack)

cacheChosenDigit:
        subi r30, lo8(negLeds)
;        sbci r31,  hi8((ALPHA << 8) - Leds)
;        sbci r31, -hi8(highHack)
;        sbci r31, -(hi8(highHack))
        sbci r31, hi8(highHak2)

,

for which the assembler gives the following errors:

Quote:
Error 1 invalid operand (.bss section) for `-' when setting `negLeds'
Error 2 invalid operand (.bss section) for `-' when setting `highHak2'

The other commented-out variants produced different accusations of "garbage at end of line" or "missing right parenthesis" complaints.

Knowing, though, that the C compiler is able to dereference arrays just fine, working through the same assembler, I fiddled around awhile building some dummy C code and discovered that I hadn't quite guessed the right arrangement of multiple parentheses. This seems to be what the assembler wants, at least for the simple low-byte of the address:

        subi r30, lo8(-(Leds))

, but it didn't extend too well to the computation of the high byte. I tried these lines

        sbci r31, hi8(0x1000-(Leds))
;        sbci r31, (16+(hi8(-(Leds))))

, and either got accused of "missing ')'" or told "expression too complex". I'm giving up trying to figure out how to coax gnu into doing arithmetic, and just coding it as:

        subi r30, lo8(-(Leds))
        ldi r31, 0
        sbci r31, hi8(-(Leds))

The inability to handle an extra added term seems like a shortcoming in the tools to me, though. Is it that the linker doesn't have a rich enough set of "fixup" operations?

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

Why are you using the - sign in front of the hi8/lo8? I guess there is a way around things (not a GAS user) but I would just get the data into some temp register and then do any required computation.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:
Is it that the linker doesn't have a rich enough set of "fixup" operations?

That sounds right. A "relocatable" value is one that can have an offset added to it in the linker. Multiplying by -1 is not adding an offset.

It does seem particularly annoying, since it would probably work fine if you had addi instead of sbi.

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

Just look at what code the avr-gcc compiler emits in such a case:

subi r24,lo8(-(Leds+100))
sbci r25,hi8(-(Leds+100))

Leds must be a symbol here, it must not be a complex expression because everything has to be encoded as RELOC for the linker. The number of RELOCs the assembler can generate and the linker will understand are limited.

The RELOCs implemented in Binutils are enough to represent the weirdest C and C++ code, thus is should be enough for assembler programs, too.

avrfreaks does not support Opera. Profile inactive.

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

SprinterSB wrote:
Just look at what code the avr-gcc compiler emits in such a case:
subi r24,lo8(-(Leds+100))
sbci r25,hi8(-(Leds+100))

Leds must be a symbol here, it must not be a complex expression because everything has to be encoded as RELOC for the linker. The number of RELOCs the assembler can generate and the linker will understand are limited.

The RELOCs implemented in Binutils are enough to represent the weirdest C and C++ code, thus is should be enough for assembler programs, too.

One would think so, I suppose. So in my case, the obvious soluton that I somehow overlooked is:
    sbci r31, hi8(-(Leds-0x100))

, or (to make it more clear that I'm compensating for R31 having a value of 16 in it prior to the operation),

    sbci r31, hi8(-(Leds-(16<<8)))

. Here's what that generated:

        subi r30, lo8(-(Leds))
 39c:	e0 50       	subi	r30, 0x00	; 0
        sbci r31, hi8(-(Leds-(16 << 8)))
 39e:	f0 4f       	sbci	r31, 0xF0	; 240

According to the link map from this build, the "Leds" array is at the base of SRAM (the map has the address as 0x802000), so the generated code is wrong. It *looks* like the tools understand arithemetic, but they're not sharp enough to realize that the negative of a negative number ought to be positive. The argument for the sbci instruction should have been 0x10, instead of 0xF0.

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

In my calculator, if Leds = 2000, Leds-(16 << 8 ) = 0x1000, - of that is 0xF000, hi8 of that is 0xF0.

JW

[What?? Captcha on this?? How is this morally or politically incorrect???]

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

I didn't count the number of zeroes in the mapped address of "Leds". I guess I thought that the "2000" was part of the subdivision of gnu's imaginary VonNeumann space into the AVR's separate memory spaces. You're right; the arithmetic was correct.