Type casting and pointers - c

I have a piece of code I am unsure on would very much appreciate a run down on its workings.
The first bit is about type casting. Can someone tell me if I'm reading the following code correctly:
#define A_TIME 0xC0500000
#define B_TIME *(UINT_8 *)(A_TIME + 0x00002909)
Is the output of this that B_TIME is a pointer to an unsigned integer of 8 bits = 0x09?
I'm unsure of how type casting works? Does it assign the 8 LSB to B_TIME? Also, I'm confused by the *(UINT_8 *)? What exactly does this mean/say? It's a pointer to a unsigned integer of 8 bits?
The second part will probably be clear to me once I know the above but I'll post it anyway:
UINT_8 Timer = 0;
Input_Time (&Timer);
#define C_TIME *(UINT_16 *)0xC0C0B000
#define MASK 0x003F
void Input_Time (UINT_8 *Time)
{
*Time = 0xC0;
*Time |= (UINT_8)((C_TIME >> 4) & MASK);
return;
}
What is the value of *Time following Input_Time function? Could someone step through the code and explain each step for me?
Apologies for the noviceness (is that a word?!) of the question.
Much appreciated.
James
EDIT:
OK, I'm happy with the above. Thanks. I'm now confused as to the following which happens within the code, after Input_Time() has been called:
#define OUT_TIME *(UINT_8 *)0xC0411297
OUT_TIME = Timer;
How is this possible? Isn't OUT_TIME the 8-bit value within the address 0XC0411297? How does that work?

The code you're looking at looks like it's accessing memory mapped registers.
B_TIME will access an 8-bit register located at address A_TIME plus the specified offset - in this case, that means 0xC0502909. What actually gets read depends on the hardware you're using. Let's break down what happens in pieces. B_TIME, wherever it is used, gets replaced with the text:
*(UINT_8 *)(A_TIME + 0x00002909)
And in turn, the A_TIME is replaced with 0xC0500000, yielding:
*(UINT_8 *)(0xC0500000 + 0x00002909)
A little working out of arithmetic gives:
*(UINT_8 *)(0xC0502909)
Which means "treat 0xC0502909 as a pointer to an 8-bit value and then dereference it".
Your second question follows the same behaviour. There is a register mapped at 0xC0C0B000 that is being read when Input_Time() is called. A 16-bit value is read from that address, is downshifted by 4, and then masked. Assuming this example 16-bit value, using letters to uniquely represent the bits:
abcdefghijklmnop
Downshifted by 4:
0000abcdefghijkl
And then the mask (3f hex is 00111111 binary) applied:
0000000000ghijkl
Then, that result is ORed with the 0xc0 (11000000 binary), yielding:
0000000011ghijkl
That value is stored back into the 8-bit passed-in-byte, returning:
11ghijkl
To the caller.
Your new example:
#define OUT_TIME *(UINT_8 *)0xC0411297
OUT_TIME = Timer;
Is writing a value to that memory address.

The value of B_TIME is a value of type UINT8.
#define B_TIME *(UINT_8 *)(A_TIME + 0x00002909)
The * operator dereferences the pointer to UINT8 in the following expression:
(UINT_8 *)(A_TIME + 0x00002909)
In the above expression the integer constant expression A_TIME + 0x00002909 is converted to a pointer to UINT8 by the mean of the cast (UINT8 *).

Related

What does this code does ? There are so many weird things

int n_b ( char *addr , int i ) {
char char_in_chain = addr [ i / 8 ] ;
return char_in_chain >> i%8 & 0x1;
}
Like what is that : " i%8 & Ox1" ?
Edit: Note that 0x1 is the hexadecimal notation for 1. Also note that :
0x1 = 0x01 = 0x000001 = 0x0...01
i%8 means i modulo 8, ie the rest in the Euclidean division of i by 8.
& 0x1 is a bitwise AND, it converts the number before to binary form then computes the bitwise operation. (it's already in binary but it's just so you understand)
Example : 0x1101 & 0x1001 = 0x1001
Note that any number & 0x1 is either 0 or one.
Example: 0x11111111 & 0x00000001 is 0x1 and 0x11111110 & 0x00000001 is 0x0
Essentially, it is testing the last bit on the number, which the bit determining parity.
Final edit:
I got the precedence wrong, thanks to the comments for pointing it out. Here is the real precedence.
First, we compute i%8.
The result could be 0, 1, 2, 3, 4, 5, 6, 7.
Then, we shift the char by the result, which is maximum 7. That means the i % 8 th bit is now the least significant bit.
Then, we check if the original i % 8 bit is set (equals one) or not. If it is, return 1. Else, return 0.
This function returns the value of a specific bit in a char array as the integer 0 or 1.
addr is the pointer to the first char.
i is the index to the bit. 8 bits are commonly stored in a char.
First, the char at the correct offset is fetched:
char char_in_chain = addr [ i / 8 ] ;
i / 8 divides i by 8, ignoring the remainder. For example, any value in the range from 24 to 31 gives 3 as the result.
This result is used as the index to the char in the array.
Next and finally, the bit is obtained and returned:
return char_in_chain >> i%8 & 0x1;
Let's just look at the expression char_in_chain >> i%8 & 0x1.
It is confusing, because it does not show which operation is done in what sequence. Therefore, I duplicate it with appropriate parentheses: (char_in_chain >> (i % 8)) & 0x1. The rules (operation precedence) are given by the C standard.
First, the remainder of the division of i by 8 is calculated. This is used to right-shift the obtained char_in_chain. Now the interesting bit is in the least significant bit. Finally, this bit is "masked" with the binary AND operator and the second operand 0x1. BTW, there is no need to mark this constant as hex.
Example:
The array contains the bytes 0x5A, 0x23, and 0x42. The index of the bit to retrieve is 13.
i as given as argument is 13.
i / 8 gives 13 / 8 = 1, remainder ignored.
addr[1] returns 0x23, which is stored in char_in_chain.
i % 8 gives 5 (13 / 8 = 1, remainder 5).
0x23 is binary 0b00100011, and right-shifted by 5 gives 0b00000001.
0b00000001 ANDed with 0b00000001 gives 0b00000001.
The value returned is 1.
Note: If more is not clear, feel free to comment.
What the various operators do is explained by any C book, so I won't address that here. To instead analyse the code step by step...
The function and types used:
int as return type is an indication of the programmer being inexperienced at writing hardware-related code. We should always avoid signed types for such purposes. An experienced programmer would have used an unsigned type, like for example uint8_t. (Or in this specific case maybe even bool, depending on what the data is supposed to represent.)
n_b is a rubbish name, we should obviously never give an identifier such a nondescript name. get_bit or similar would have been a better name.
char* is, again, an indication of the programmer being inexperienced. char is particularly problematic when dealing with raw data, since we can't even know if it is signed or unsigned, it depends on which compiler that is used. Had the raw data contained a value of 0x80 or larger and char was negative, we would have gotten a negative type. And then right shifting a negative value is also problematic, since that behavior too is compiler-specific.
char* is proof of the programmer lacking the fundamental knowledge of const correctness. The function does not modify this parameter so it should have been const qualified. Good code would use const uint8_t* addr.
int i is not really incorrect, the signedness doesn't really matter. But good programming practice would have used an unsigned type or even size_t.
With types unsloppified and corrected, the function might look like this:
#include <stdint.h>
uint8_t get_bit (const uint8_t* addr, size_t i ) {
uint8_t char_in_chain = addr [ i / 8 ] ;
return char_in_chain >> i%8 & 0x1;
}
This is still somewhat problematic, because the average C programmer might not remember the precedence of >> vs % vs & on top of their head. It happens to be % over >> over &, but lets write the code a bit more readable still by making precedence explicit: (char_in_chain >> (i%8)) & 0x1.
Then I would question if the local variable really adds anything to readability. Not really, we might as well write:
uint8_t get_bit (const uint8_t* addr, size_t i ) {
return ((addr[i/8]) >> (i%8)) & 0x1;
}
As for what this code actually does: this happens to be a common design pattern for how to access a specific bit in a raw bit-field.
Any bit-field in C may be accessed as an array of bytes.
Bit number n in that bit-field, will be found at byte n/8.
Inside that byte, the bit will be located at n%8.
Bit masking in C is most readably done as data & (1u << bit). Which can be obfuscated as somewhat equivalent but less readable (data >> bit) & 1u, where the masked bit ends up in the LSB.
For example lets assume we have 64 bits of raw data. Bits are always enumerated from 0 to 63 and bytes (just like any C array) from index 0. We want to access bit 33. Then 33/8 integer division = 4.
So byte[4]. Bit 33 will be found at 33%8 = 1. So we can obtain the value of bit 33 from ordinary bit masking byte[33/8] & (1u << (bit%8)). Or similarly, (byte[33/8] >> (bit%8)) & 1u
An alternative, more readable version of it all:
bool is_bit_set (const uint8_t* data, size_t bit)
{
uint8_t byte = data [bit / 8u];
size_t mask = 1u << (bit % 8u);
return (byte & mask) != 0u;
}
(Strictly speaking we could as well do return byte & mask; since a boolean type is used, but it doesn't hurt to be explicit.)

ARM Cortex M4 - C Programming and Memory Access Optimization

The following three lines of code are optimized ways to modify bits with 1 MOV instruction instead of using a less interrupt safe read-modify-write idiom. They are identical to each other, and set the LED_RED bit in the GPIO Port's Data Register:
*((volatile unsigned long*)(0x40025000 + (LED_RED << 2))) = LED_RED;
*(GPIO_PORTF_DATA_BITS_R + LED_RED) = LED_RED;
GPIO_PORTF_DATA_BITS_R[LED_RED] = LED_RED;
LED_RED is simply (volatile unsigned long) 0x02. In the memory map of this microcontroller, the first 2 LSBs of this register (and others) are unused, so the left shift in the first example makes sense.
The definition for GPIO_PORTF_DATA_BITS_R is:
#define GPIO_PORTF_DATA_BITS_R ((volatile unsigned long *)0x40025000)
My question is: How come I do not need to left shift twice when using pointer arithmetic or array indexing (2nd method and 3rd method, respectively)? I'm having a hard time understanding. Thank you in advance.
Remember how C pointer arithmetic works: adding an offset to a pointer operates in units of the type pointed to. Since GPIO_PORTF_DATA_BITS_R has type unisgned long *, and sizeof(unsigned long) == 4, then GPIO_PORTF_DATA_BITS_R + LED_RED effectively adds 2 * 4 = 8 bytes.
Note that (1) does arithmetic on 0x40025000, which is an integer, not a pointer, so we need to add 8 to get the same result. Left shift by 2 is the same as multiplication by 4, so LED_RED << 2 again equals 8.
(3) is exactly equivalent to (2) by definition of the [] operator.

Why could I do a right shift by two bits to access to the GPIO at the odroid board?

Here is an example of a C source code to deal with the GPIO at the odroid XU3 board. My question is what is the >> 2 intended for in the constructions like this one:
*(gpio + (0x0c24 >> 2)) |= (1 << 2);
gpio is a uint32_t pointer.
The address 0x24 is given on a byte wise basis.
When we add 1 to 32 bit pointer, the address jumps 4 locations. So, we have to add the address/4 to get to the correct position. (>>2 is hte same as dividing by 4)
It's because the variable is declared like so:
static volatile uint32_t *gpio;
So it's a pointer to 32-bit unsigned integers. This means that if we add 1 in the code to the pointer, the actual address referenced will be 4 bytes later than before. This is how pointer arithmetic works in C.
So, to use the byte offset 0xc24 from the base, that offset needs to be scaled down by four, which is what a shift right of two bits does.
It could just as well be written:
*(gpio + 0xc24 / 4) |= (1 << 2);
But often you see shifts used for powers of two. Modern compilers do this optimization easily, you won't see a division in the code for this (or even a shift, since the term can be computed compile-time).

Changing one given bit in a binary address in C

I'm working on the "buddy-allocation" for a memory management project in C (see page 14 of this .pdf).
I'd like to find the "buddy" of a given address, knowing that the two buddies are only one-bit-different (the size of the chunk tells us which bit changes). For example, if one of the two 32-bits buddy chunks has the binary address 0b110010100, the second one will be located at 0b110110100 (the 6th bit from the right changes, as 32=2^(6-1)).
I'd like to implement that in C, without exponentiation algorithms because I'm trying to make my program as fast-executing as possible. At best I'd use a tool to manipulate bits, if that exists. Any hints?
EDIT: the type of the addresses is void*. With the solutions posted below, gcc won't let me compile.
EDIT2: I've tried the answers posted below with the XOR operator, but I can't compile because of the type of the addresses. Here's what I've tried :
void* ptr1 = mmap(NULL, 640000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FILE | MAP_PRIVATE, -1, 0);
printf("%p\n", ptr1);
void* ptr2 = ptr1+0x15f6d44;
printf("%p\n", ptr2);
void* ptr3 = (void*)(ptr2-ptr1);
printf("%p\n", ptr3);
void* ptr4 = ptr3 ^ (1 << 6);
printf("%p\n", ptr4);
and the gcc error :
invalid operands to binary ^ (have ‘void *’ and ‘int’)
It looks like you just want to toggle a given bit, which is achieved using an XOR operation:
buddy_adr = (unsigned long)adr ^ (1 << bit_location);
The cast to unsigned long is required to avoid errors of undefined XOR operation on type void*.
Depending on your compiler settings, you may also get a warning about creating a pointer (i.e., an address) by casting an integer, which is obviously dangerous in the general case (you could pass an invalid address value). To silent this warning, cast back the result to void* to let the compiler know that you know what you are doing:
buddy_adr = (void *)((unsigned long(adr ^ (1 << bit_location));
Note that in embedded system programming (where I've used this technique most of the time since many peripherals are memory-mapped) you would usually "simplify" this line of code using macros like TOGGLE_BIT(addr, bit) and INT_TO_ADDR(addr).
You can set one bit with a | bitwise or.
adr = adr | 0x10;
A tool? To manipulate bits? You don't need a "tool", that's about as primitive an operation as you can do.
uint32_t address = 0x0194;
address |= 1 << 5; /* This sets the sixth bit. */
If you really want to toggle the bit, i.e. set if if it's clear, but clear it if it's set, you use the bitwise XOR operator:
address ^= 1 << 5;
This is not "exponentiation", it's just a bitwise XOR.
If the address is held in a pointer register, either cast or copy to integer (uintptr_t) and the copy back.
This is case of bit manipulation which is very common in c programming
if you want to change xxbxxxxx simply XOR this with xx1xxxxx. XOR topple the given bit. If you want to make it 1 just use OR (|) with all bits 0 except that bit 1 which you want to turn on
a more compact way to do this
#define BIT_ON(x,bit) (x |= ( 1 << (bit-1)) )
#define BIT_TOGGLE(x,bit) (x ^= ( 1 << (bit-1)) )
#define BIT_OFF(x,bit) (x &= ~( 1 << (bit-1)) )

C programming: words from byte array

I have some confusion regarding reading a word from a byte array. The background context is that I'm working on a MIPS simulator written in C for an intro computer architecture class, but while debugging my code I ran into a surprising result that I simply don't understand from a C programming standpoint.
I have a byte array called mem defined as follows:
uint8_t *mem;
//...
mem = calloc(MEM_SIZE, sizeof(uint8_t)); // MEM_SIZE is pre defined as 1024x1024
During some of my testing I manually stored a uint32_t value into four of the blocks of memory at an address called mipsaddr, one byte at a time, as follows:
for(int i = 3; i >=0; i--) {
*(mem+mipsaddr+i) = value;
value = value >> 8;
// in my test, value = 0x1084
}
Finally, I tested trying to read a word from the array in one of two ways. In the first way, I basically tried to read the entire word into a variable at once:
uint32_t foo = *(uint32_t*)(mem+mipsaddr);
printf("foo = 0x%08x\n", foo);
In the second way, I read each byte from each cell manually, and then added them together with bit shifts:
uint8_t test0 = mem[mipsaddr];
uint8_t test1 = mem[mipsaddr+1];
uint8_t test2 = mem[mipsaddr+2];
uint8_t test3 = mem[mipsaddr+3];
uint32_t test4 = (mem[mipsaddr]<<24) + (mem[mipsaddr+1]<<16) +
(mem[mipsaddr+2]<<8) + mem[mipsaddr+3];
printf("test4= 0x%08x\n", test4);
The output of the code above came out as this:
foo= 0x84100000
test4= 0x00001084
The value of test4 is exactly as I expect it to be, but foo seems to have reversed the order of the bytes. Why would this be the case? In the case of foo, I expected the uint32_t* pointer to point to mem[mipsaddr], and since it's 32-bits long, it would just read in all 32 bits in the order they exist in the array (which would be 00001084). Clearly, my understanding isn't correct.
I'm new here, and I did search for the answer to this question but couldn't find it. If it's already been posted, I apologize! But if not, I hope someone can enlighten me here.
It is (among others) explained here: http://en.wikipedia.org/wiki/Endianness
When storing data larger than one byte into memory, it depends on the architecture (means, the CPU) in which order the bytes are stored. Either, the most significant byte is stored first and the least significant byte last, or vice versa. When you read back the individual bytes through byte access operations, and then merge them to form the original value again, you need to consider the endianess of your particular system.
In your for-loop, you are storing your value byte-wise, starting with the most significant byte (counting down the index is a bit misleading ;-). Your memory looks like this afterwards: 0x00 0x00 0x10 0x84.
You are then reading the word back with a single 32 bit (four byte) access. Depending on our architecture, this will either become 0x00001084 (big endian) or 0x84100000 (little endian). Since you get the latter, you are working on a little endian system.
In your second approach, you are using the same order in which you stored the individual bytes (most significant first), so you get back the same value which you stored earlier.
It seems to be a problem of endianness, maybe comes from casting (uint8_t *) to (uint32_t *)

Resources