0

What does this piece of code mean?

#define kb_Data \
(uint8_t)((volatile uint16_t*)0xF50010)

I understand what a #define statement does, but what does it mean when it casts a uint16_t pointer to a uint8_t integer? This is meant to be an array of bytes, as can be seen in here, but I don't understand how an int can act as an array

feverdreme
  • 467
  • 4
  • 12
  • 6
    It looks like nonsense. – Pete Becker Jun 06 '21 at 02:39
  • 3
    This looks like **shenanigans with operator precedence**. When you write `kb_Data[0]` as shown on that webpage, the cast `(uint8_t)` takes place after the subscripting. – Ben Voigt Jun 06 '21 at 02:43
  • 1
    Basically, it is the same as `inline uint8_t get_kb_Data(int i) { constexpr volatile uint16_t* table = (uint16_t*)0xF50010; return table[i]; }` but far far more troublesome. – Ben Voigt Jun 06 '21 at 02:45
  • ok so it first interprets it as an array starting at 0xF50010 but then after retrieving the element it then casts to a uint8_t ? – feverdreme Jun 06 '21 at 02:46
  • @feverdream: Yes. As long as it is used exactly as `kbData[i]`. If you do anything that should be equivalent under the rules of the C language, like `(kbData)[i]` or `*(kbData + i)` it all goes haywire. – Ben Voigt Jun 06 '21 at 02:47
  • 1
    @BenVoigt: That's the answer (post it!), but man, without looking at how it was used, I was flummoxed as hell by this. Who casts a pointer to a `uint8_t`?!? Apparently they don't, as long as every use of the macro uses it correctly. Ugh that's terrible. – ShadowRanger Jun 06 '21 at 02:52
  • @ShadowRanger: I think I've described what it does. As for the question asked, which is what it means, my best guess without trying to put myself into the twisted mind of the author is "The writer of this code hates everyone who will ever try to read or use this macro". But it is entirely possible that [the writer is aware that he has a co-worker who knows where he lives, and is hoping to have his homeowner's insurance policy pay out.](https://stackoverflow.com/q/876089/103167) – Ben Voigt Jun 06 '21 at 03:03
  • @BenVoigt my theory is that this is slightly faster and more efficient than using a function or creating an extra variable as ShadowRanger wrote. This code is compiled and meant to be run on te ti84 plus ce calculator, which is significantly less powerful than your average computer – feverdreme Jun 06 '21 at 03:06
  • 1
    @feverdream: It is not faster than an inline function. The compiler may take slightly longer to deal with an inline function, but the resulting machine code (what actually gets loaded to the calculator) would not be any different. – Ben Voigt Jun 06 '21 at 03:08
  • I have to say I think you are overstating how bad this is, kbData[i] and *(kbData + i) are not generally equivilent in C++ anyway, and even if someone did do *(kbData + i) or (kbData)[i] it would be a compile time error, not any kind of hard to debug runtime error. – plugwash Jun 06 '21 at 22:45

1 Answers1

3

Taken as a complete expression it's nonsense. I just did some testing and on g++ and clang++ by default it's a compile error. On g++ with -fpermissive it compiles and produces the value 0x10.

However macros are not necessarily complete expressions, they are text substitutions. As was pointed out by Ben Voigt in a comment this allows shenanigans with operator precedence.

So if someone writes something like.

a = kb_Data[2];

Then, because array indexing has a higher precedence than a C-style cast, it will read element 2 of an array of 16 bit numbers at address 0xf50010. Then convert the result to an 8 bit number.

So why would you do this? My best guess would be someone hooked up an 8 bit peripheral to the low bits of a 16 bit bus rather than implement a proper bus bridge, then used a macro hack to make the driver work.

plugwash
  • 9,724
  • 2
  • 38
  • 51