Reputation: 13
I am currently working on writing a basic operating system as a learning project. I use a gcc 4.9.2 cross compiler for this purpose. When trying to use memory-mapped I/O I stumbled upon behaviour of C pointers (or possibly memory-mapped I/O) which I fail to understand. When directly accessing the I/O memory through following code, I get the expected result, which is an "AB" in the top left corner in light grey font on black background.
*((uint16_t *)0xB8000) = 0x0741;
*((uint16_t *)0xB8002) = 0x0742;
However when trying to manipulate the memory through the use of an offset adding 2 to the base address the result is not as expected an "A" in second but in third position.
*((uint16_t *)0xB8000 + 2) = 0x0741;
Adding 1 instead of 2 prints out the letter in second position, however I do not understand why. Since every letter (in MMIO) is composed of 2 bytes of data I would assume that I need to increase the memory address which I write to by 2 for the next character. (As I did at first by writing directly to 0xB8002) In order to try and understand this behaviour I did some testing in a separate C program but was unable to replicate this behaviour: (Note: This code was compiled using normal gcc 4.8.2)
#include <stdint.h>
#include <stdio.h>
void main(void) {
printf("0xB8000 + 1 = %x\n", 0xB8000 + 1);
printf("&(*(uint16_t *)(0xB8000 + 1)) = %x\n", (uint32_t)&(*(uint16_t *)(0xB8000 + 1)));
}
This program produced following output:
0xB8000 + 1 = b8001
&(*(uint16_t *)(0xB8000 + 1)) = b8001
I assume that I am missing some kind of behaviour of C pointers which causes the factoring in of the size of data which is written by a statement. Is this the case or is there a different reason that I have overlooked?
Regards, Kenshooak
Upvotes: 1
Views: 255
Reputation: 9894
If you add an integer n
to a <type>
pointer the address is incremented by sizeof( <type> ) * n
bytes.
So in your first example, you have
*((uint16_t *)0xB8000 + 2) = 0x0741;
so what happens here is: 0xB8000 is cast to unit16_t *
and then that pointer is incremented by 2 which means 2 * sizeof( uint16_t )
= 4 Bytes
In your second example you have
*(uint16_t *)(0xB8000 + 1)
Notice the different bracketing: here you first add 1 to 0xB8000 -- a simple integer operation -- and cast the result.
Upvotes: 4