check range of device number Wait until READY or ERROR if no error read byte

Check range of device number wait until ready or

This preview shows page 250 - 253 out of 359 pages.

* check range of device number. * Wait until READY or ERROR * if no error, read byte, return it * otherwise reset error, return 0xffff */ unsigned int read_dev(unsigned devno){ struct devregs * const dvp = DEVADDR + devno; if(devno >= NDEVS)
Image of page 250
242 CHAPTER 8. SPECIALIZED AREAS OF C return(0xffff); while((dvp->csr & (READY | ERROR)) == 0) ; /* NULL - wait till done */ if(dvp->csr & ERROR){ dvp->csr = RESET; return(0xffff); } return((dvp->data) & 0xff); } Example 8.5 The rules about mixing volatile and regular types resemble those for const . A pointer to a volatile object can be assigned the address of a regular object with safety, but it is dangerous (and needs a cast) to take the address of a volatile object and put it into a pointer to a regular object. Using such a derived pointer results in undefined behaviour. If an array, union or structure is declared with const or volatile attributes, then all of the members take on that attribute too. This makes sense when you think about it–how could a member of a const structure be modifiable? That means that an alternative rewrite of the last example would be possible. Instead of declaring the device registers to be volatile in the structure, the pointer could have been declared to point to a volatile structure instead, like this: struct devregs{ unsigned short csr; /* control \& status */ unsigned short data; /* data port */ }; volatile struct devregs *const dvp=DEVADDR+devno; Since dvp points to a volatile object, it not permitted to optimize references through the pointer. Our feeling is that, although this would work, it is bad style. The volatile declaration belongs in the structure: it is the device registers which are volatile and that is where the information should be kept; it reinforces the fact for a human reader. So, for any object likely to be subject to modification either by hardware or asynchronous interrupt service routines, the volatile type qualifier is impor- tant. Now, just when you thought that you understood all that, here comes the final twist. A declaration like this:
Image of page 251
8.4. CONST AND VOLATILE 243 volatile struct devregs{ /* stuff */ }v_decl; declares the type struct devregs and also a volatile -qualified object of that type, called v decl . A later declaration like this struct devregs nv_decl; declares nv decl which is not qualified with volatile ! The qualification is not part of the type of struct devregs but applies only to the declaration of v decl . Look at it this way round, which perhaps makes the situation more clear (the two declarations are the same in their effect): struct devregs{ /* stuff */ }volatile v_decl; If you do want to get a shorthand way of attaching a qualifier to another type, you can use typedef to do it: struct x{ int a; }; typedef const struct x csx; csx const_sx; struct x non_const_sx = {1}; const_sx = non_const_sx; /* error - attempt to modify a const */ 8.4.2.1 Indivisible Operations Those of you who are familiar with techniques that involve hardware inter- rupts and other ‘real time’ aspects of programming will recognise the need for volatile types. Related to this area is the need to ensure that accesses to data objects are ‘atomic’, or uninterruptable. To discuss this is any depth
Image of page 252
Image of page 253

  • Left Quote Icon

    Student Picture

  • Left Quote Icon

    Student Picture

  • Left Quote Icon

    Student Picture