From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,610e53a911ec64b3 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1995-03-24 12:20:58 PST Path: nntp.gmd.de!Germany.EU.net!howland.reston.ans.net!spool.mu.edu!bloom-beacon.mit.edu!boulder!news.coop.net!news.den.mmc.com!iplmail.orl.mmc.com!alcyone!rgilbert From: rgilbert@orl.mmc.com (Bob Gilbert) Newsgroups: comp.lang.ada Subject: Re: Importing C Structures Date: 24 Mar 1995 20:20:58 GMT Organization: Martin Marietta Orlando Distribution: world Message-ID: <3kv9ja$6h3@theopolis.orl.mmc.com> References: <3ksg66$h1c@newssvr.cacd.rockwell.com> Reply-To: rgilbert@orl.mmc.com NNTP-Posting-Host: alcyone.orl.mmc.com Date: 1995-03-24T20:20:58+00:00 List-Id: In article h1c@newssvr.cacd.rockwell.com, rswhite@cacd.rockwell.com (Robert S. White) writes: -> ->Do you have a clean way to handle hardware registers that have one ->meaning when you read them and another when you write to them with ->nice seperate names for each type of usage? I know "use at" for ->Ada 83 can overide this but it seems to be not in the spirit of ->the 83 LRM; -> 13.5 "Address clauses should not be used to achieve overlays of -> objects...Any program using address clauses to achieve -> such effects is erroneous." ->A work around is to have a dual meaning name for the register and ->use one procedure, appropriately named, to write to it and a function, ->again with a suitable name, for reading it. -> ->This is a case where C programmers use the union structure. I ->confess that I have not ferreted out in the Ada 95 RM or Rational ->the right (and very concise) way to do this now. -> ->Recommendations? Well, using Ada 83 there are a couple of ways. You are correct about avoiding the address clause to "overlay" two or more objects to the same memory location. If the location has different meaning when read from that when written to, I would use Unchecked_Conversion and do something like the following: package Whatever_Interface is type READ_WHATEVER is record ..... end record; type WRITE_WHATEVER is record .... end record; procedure Write(Val : in WRITE_WHATEVER); function Read return READ_WHATEVER; end Whatever_Interface; with System; with Unchecked_Conversion; package body Whatever_Interface is Whatever : WRITE_WHATEVER; for Whatever use at {put address here}; procedure Write(Val : in WRITE_WHATEVER) is begin Whatever := Val; end Write; function Read return READ_WHATEVER is function To_Read_Type is new Unchecked_Conversion(WRITE_WHATEVER, READ_WHATEVER); begin return To_Read_Type(Whatever); end Read; end Whatever_Interface; The advantage of using the packaging and procedure/function to access the location is that it allows protection of the object. You can only write a valid type to it as well as returning the proper type when using the function to read the object. You can use pragma Inline if there are concerns about the overhead in making these simple calls. Most reasonable compilers will optimize the code generated such that it will appear to give direct access to the memory location. Of course if you really want to "overlay" two seperate objects to the same location, you can probably make the assumption that access types are implemented as addresses, and you could then do the following: with System; with Unchecked_Conversion; package Whatever_Interface is Location : constant System.ADDRESS := {put address here}; type READ_WHATEVER is record ..... end record; type WRITE_WHATEVER is record .... end record; type READ_WHATEVER_PTR is access READ_WHATEVER; type WRITE_WHATEVER_PTR is access WRITE_WHATEVER; function To_Ptr is new Unchecked_Conversion(System.ADDRESS, READ_WHATEVER_PTR); function To_Ptr is new Unchecked_Conversion(System.ADDRESS, WRITE_WHATEVER_PTR); Read_Val : constant READ_WHATEVER_PTR := To_Ptr(Location); Write_Val : constant WRITE_WHATEVER_PTR := To_Ptr(Location); end Whatever_Interface; Of course you have to remember to use the ".all" notation to make sure you are referencing the value "pointed" to by the access value(s). This is probably considered bastardized Ada code. It is dangerous since you can write a read type value (Read_Val := xxx) and the compiler will offer no protection. It also relies on the knowlege that access types are implemented as an address (not required by the LRM, but usually true). Note that at least I made the access (pointer) values constants so that they are protected from accidental modification (also stating that something you intend to be constant with the key word "constant" frequently allows the compiler to make advantageous assumptions during optimization). Now with Ada 83 the first example may have a problem using the address clause. Many compilers will perform a write to the location during elaboration (I guess to insure that any initialization that might have been specified is performed). This is the reason I used the WRITE_WHATEVER type to define the object. If this should be a problem, you can use the access type to map the object in the same style as done in the second example. As for Ada 95, I'll quote Tucker Taft from a previous post: -------------------------------------------------------------- In Ada 95, you can specify "pragma Import(Ada, );" which indicates that *no* default initialization is to be performed by the compiler-generated code. This is generally desirable when using an address clause to associate a name with a machine register of some sort. Also, in Ada 95 there are language-defined functions for converting integers to addresses. Finally, the preferred syntax for specifying the address of an object uses "for ...'Address use ...", such as: for Console_Status_Register'Address use System.Storage_Elements.To_Address(16#FFC0#); The "for ... use at ..." syntax is still retained for upward compatibility reasons. -------------------------------------------------------------- -Bob