kenshooak
kenshooak

Reputation: 13

Strange behaviour of C pointers with memory mapped I/O

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

Answers (1)

Ingo Leonhardt
Ingo Leonhardt

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

Related Questions