3

I'm trying to write my own little kernel in C, and actually I would like to write a print function to display string. Thus, I would like to write to the video memory (at 0xB8000).

So, I tried like this :

unsigned char *video = (unsigned char *)0xB8000;

*video = 'A';

This actually works, but following does not :

char x = 0;
unsigned char *video = (unsigned char *)(0xB8000 + x);

*video = 'A';

After a few research, I fould that the cause could be the compiler's optimizations, and OSDev give me the solution : use the volatile keyword. So I made some research about this keyword. OSDev suggest this :

char x = 0;
volatile unsigned char *video = (volatile unsigned char *)(0xB8000 + x);

*video = 'A';

This way, it should work. The compiler assumes that the value pointed to by video pointer can change, and then do not optimize it. But, if later I want to change it, like :

video = (volatile unsigned char *)(video + 2);

Should I not define the pointer as volatile, like unsigned char * volatile video ? So the compiler know that the address can change ?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Quentin
  • 724
  • 7
  • 16
  • 1
    I did not understood what was not working without volatile? `unsigned char *video = (unsigned char *)(0xB8000 + x);`. I suppose the problem is that `x` is `char` not `int/unsigned`. The type can be the problem, not non-volatility – i486 Aug 26 '15 at 08:49
  • 1
    @i486 If the compiler is able to deduce that the contents of `video` are never read, it might optimize away the writes to it. – nemetroid Aug 26 '15 at 08:51
  • 1
    And why `(unsigned char *)(0xB8000 + x)` will not write the value to video memory and `(unsigned char *)0xB8000` will do it? I don't think there can be such optimization. Only the value of `video` can be held in CPU register instead its memory location. – i486 Aug 26 '15 at 08:53
  • 1
    I will agree that it's odd, but the fact that optimization doesn't happen in the first case isn't *really* an argument that it won't happen in the second case. – nemetroid Aug 26 '15 at 08:59
  • Note that while the use of `volatile` here is (probably) valid, you should read [this](https://lwn.net/Articles/233482/) before using it all over your kernel. A better way, rather than declaring `video` `volatile`, is to cast all accesses that need to be volatile to `volatile`. – Kninnug Aug 26 '15 at 09:01
  • @i486 why do you think there's no such optimization. It's one of the most basic type of optimization: removing unnecessary code, and unread writes are one of that. If you read a microcontroller register header you'll see that all registers must be volatile – phuclv Aug 26 '15 at 09:31
  • @Lưu Vĩnh Phúc I think you cannot understand about what optimization is the conversation. The value of `video` can be optimized and stay in register without problem. The values stored in video memory (address 0xB8000 and above) can be "optimized" only by the CPU cache. Only delayed cache writes can cause problem and not show the video output. But it cannot be controlled by the compiler. – i486 Aug 26 '15 at 09:34
  • @Kninnug thank you for the link, it make sens, I'll try to apply it :) – Quentin Aug 26 '15 at 10:21
  • @i486 no. I think you cannot understand about optimization and volatile. Only read/writes with `volatile` are not permitted to optimize out, otherwise the keyword is meaningless as the compiler will always have to emit memory accesses. [What kinds of optimizations does 'volatile' prevent in C++?](http://stackoverflow.com/q/3604569/995714) https://en.wikipedia.org/wiki/Volatile_%28computer_programming%29 http://stackoverflow.com/a/246392/995714 – phuclv Aug 26 '15 at 10:27
  • @LưuVĩnhPhúc: Many vendor-supplied hardware-register headers which define pointers using hard-coded addresses omit `volatile`, Having compilers simply treat objects from integer-to-pointer conversions as `volatile`, and treat accesses to them as potentially aliasing any other object of that type, is a lot simpler for a compiler than doing anything else; from a practical standpoint, I question whether the optimization benefits of tracking integer-based pointers would pay off often enough to make it worthwhile, even if one didn't mind having to lengthen the header file by 8 bytes/declaration. – supercat Sep 29 '17 at 08:06

2 Answers2

5

The point of volatile is to tell the compiler that the contents of the variable might change or have side-effects outside the execution of your code. As the standard (C99 6.7.3.6) puts it:

An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. What constitutes an access to an object that has volatile-qualified type is implementation-defined.

That's why you need to tell the compiler that the memory pointed to by the variable is volatile. However, changing what address the pointer points at does not cause any side effects and won't change by itself outside the execution of the code, so there's no reason for the pointer itself to be volatile. If we had to mark all variables we wish to change at some point as volatile, almost all variables would have to be marked as such.

nemetroid
  • 2,100
  • 13
  • 20
0

Why not:

unsigned char * video = 0xB8000;
video[x] = 'A';
i486
  • 6,491
  • 4
  • 24
  • 41
  • Sure, it is possible, and maybe the best way to do it, but it does not explain to me what's behind my _volatile_ problem ;) – Quentin Aug 26 '15 at 08:46
  • This doesn't work. The compiler might optimize it away. And casting the address literal is better – phuclv Aug 26 '15 at 08:52