| I'm having troubles while reading I/O ports!

Previous | Input: Keyboard and Link |

Q:

I have a lot of troubles while reading I/O ports. I need a loop which will wait until the programable timer on the TI-89 reaches value 255. I know that I can read the value of the programable timer counter using address 0x600017, so I made the following loop:

    while (*(unsigned char *)0x600017 != 255);

But, the calculator freezes. I tried to use peek macro, as in

    while (peek (0x600017) != 255);

No fortune again (I know that this is, in fact, the same as above). What is wrong?

A:

See, there is a problem when reading I/O ports in small loops. Look at the above loop for example. The compiler will notice that the same value is read again and again in the loop. As normal memory location can not be changed without explicitly writing to it, and there is nothing in the loop which changes the address 0x600017, the optimizer will move memory reading out of the loop to make the code more efficient. Such behavior is correct for ordinary memory locations. But, in our case, it will cause an infinity loop. The compiler does not know anything about the fact that 0x600017 is not an ordinary memory location but an I/O port, which may be changed unpredictably (purely by the hardware, without any program control). To prevent such behaviour, you should use the `volatile` keyword to make the address 'volatile', i.e. to say to the compiler that this address may be changed unpredictably. So, the correct code is

    while (*(volatile unsigned char *)0x600017 != 255);

Starting from TIGCCLIB 2.3, a new macro peekIO is introduced, to make life easier. You can do

    while (peekIO (0x600017) != 255);

Basically, peekIO works exactly like peek, but prevents any unwanted optimizations generated by the compiler. Always use peekIO (or peekIO_w) for reading memory-mapped I/O ports, else you may have troubles. The _rowread function in TIGCCLIB releases prior to 2.3 also caused similar troubles, now this is corrected too.
peekIO may be used even for reading bytes into the memory, but peek will generate better code when working with memory. However, use peekIO to read any memory location which may change in a way which is unpredictable from the aspect of a normal program flow (for example, a memory location which is changed in the body of the interrupt handler).
Note: For sending bytes to I/O ports, the macros pokeIO and pokeIO_w are also introduced, which are more reliable for this purpose than poke and poke_w.