home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / programm / prog3 / chap30.txt < prev    next >
Encoding:
Text File  |  1991-07-01  |  22.7 KB  |  529 lines

  1.  
  2.  
  3.  
  4.                                                        Chapter 30
  5.                                        MACHINE DEPENDENT FEATURES
  6.  
  7.  
  8. In this chapter we will cover some of the constructs available with
  9. Ada that give you the ability to get into real trouble, because we
  10. will be using the low level features of Ada.  The low level
  11. features are those that allow us to get down to the inner workings
  12. of the computer, but we will be able to get to them by use of
  13. rather high level Ada abstractions.
  14.  
  15.  
  16. OVERRIDING COMPILER DEFAULTS
  17. _________________________________________________________________
  18.  
  19. Normally, the compiler will make many decisions for us about how
  20. to store data, and how to operate on it.  Occasionally, we wish to
  21. tell the compiler that we are not satisfied with the way it
  22. defaults something, and we wish for it to use a different means of
  23. representation.  The topics examined in this chapter will give us
  24. the ability to tell the compiler how to map something onto the
  25. underlying hardware.  We gain control of how the compiler
  26. represents some things within the machine, but we also may make the
  27. resulting program nonportable.  This can cause many problems if we
  28. ever wish to move our program to another computer.  In addition,
  29. we may affect the size or speed of the resulting code, since the
  30. compiler writer will use a certain method of representing data
  31. because it results in some form of savings on the particular target
  32. machine.
  33.  
  34.  
  35. USE REPRESENTATION CLAUSES SPARINGLY
  36. _________________________________________________________________
  37.  
  38. In general, the use of the representation clauses which will be
  39. discussed in this chapter, should be used very sparingly if they
  40. are used at all.  In any case, it would be best to delay using any
  41. of these constructs until the program is fairly well developed,
  42. because correct operation of the overall program is far more
  43. important than generating tight efficient code.  After the program
  44. is debugged and operating as desired, it is usually a simple matter
  45. to go back and tighten up the size and speed of the most heavily
  46. used sections of code.  You may find that after you get the program
  47. working in a macro sense, it is fast enough and compact enough that
  48. it is not necessary to resort to these techniques.
  49.  
  50. With all of these words telling you not to use these programming
  51. techniques, we will now take a look at how to use some of them.
  52. Keep in mind however, that when you use these Ada constructs, you
  53. may lose the ability to port your program to another
  54. implementation.  You will also find that the example programs in
  55. this chapter are those that are most likely to cause problems with
  56. your compiler.
  57.  
  58.                                                         Page 30-1
  59.  
  60.                           Chapter 30 - Machine Dependent Features
  61.  
  62.  
  63. THE REQUIRED PACKAGE NAMED SYSTEM
  64. _________________________________________________________________
  65.  
  66. According to the LRM, your compiler must have a standard package
  67. named System which must be defined in Appendix F of the
  68. documentation supplied with your compiler.  Section 13.7 of the LRM
  69. contains a minimum list of items that must be defined for you in
  70. the package specification for System.  It would be profitable for
  71. you to spend a few minutes studying the definition of the package
  72. named System in your compiler documentation and the required items
  73. listed in the LRM.  You will find several constants defined there
  74. that you have been using throughout this tutorial, and now you will
  75. know where they came from.
  76.  
  77.  
  78.  
  79. WHAT REPRESENTATION CONTROLS ARE THERE?
  80. _________________________________________________________________
  81.  
  82. When we attempt to control the representation of data and the way
  83. it is stored, we are actually telling the Ada compiler how to
  84. define a type.  It would be a little more precise to say we are
  85. telling the compiler how to modify its default method of storing
  86. a certain type, including which parameters we want it to change,
  87. and what to change them to.
  88.  
  89. There are four different areas of Ada typing that can be specified
  90. with representation clauses, and we will look at each area in
  91. succession.  They are listed in no particular order as follows.
  92.  
  93.      Length specification
  94.      Record type representation
  95.      Enumeration type representation
  96.      Address specification
  97.  
  98. You will note that in each example, we will first declare the basic
  99. type, then we will tell the Ada compiler what parameters we wish
  100. to modify to suit our purposes, and finally we will declare objects
  101. of the modified type.  We will mention this order again in some of
  102. the following example programs.
  103.  
  104.  
  105.  
  106. THE LENGTH SPECIFICATION
  107. _________________________________________________________________
  108.  
  109. The length specification is used to declare how  ================
  110. many bits can be used to store data in a certain   SMALLINT.ADA
  111. type.  This representation clause is illustrated ================
  112. in the example program named SMALLINT.ADA, which
  113. you should examine at this time.
  114.  
  115.  
  116.  
  117.                                                         Page 30-2
  118.  
  119.                           Chapter 30 - Machine Dependent Features
  120.  
  121. The only code that is different in this program is found in lines
  122. 9 through 12.  First we define a constant of value 1 named BITS to
  123. be used later for a very good reason.  Next we declare a derived
  124. type which covers a range of -25 through 120, a range small enough
  125. to be represented with only 8 bits.  Since we wish to declare a
  126. rather large array of this type, and we suspect that our compiler
  127. will simply assign a full word of 16 bits or more to each variable
  128. of this type, we tell the compiler that we want it to use only 8
  129. bits to store a variable of this type.  Line 11 is a representation
  130. clause to do this.  It begins with the reserved word for followed
  131. by the type with a tick and the word SIZE.  It looks like an
  132. attribute, and that is just what it is, because we are telling the
  133. compiler that we want the attribute named SIZE to have the value
  134. of 8.
  135.  
  136. The use of the constant should now be clear.  It makes the
  137. expression extremely clear because when you read the expression it
  138. says just what it is doing.  You should not be bothered that using
  139. this construct will slow down the program, because it will not.
  140. This constant will be evaluated only once, and that will be at
  141. compile time, not when the program is executing.
  142.  
  143.  
  144.  
  145. YOUR COMPILER MAY NOT LIKE THIS CLAUSE
  146. _________________________________________________________________
  147.  
  148. The LRM does not require that an Ada compiler implement every
  149. representation clause.  For this reason, even though you have a
  150. validated Ada compiler, it may not implement line 11.  If it
  151. doesn't, it will give you a message during compilation that it
  152. cannot handle this representation clause and will fail to give you
  153. an object module.  You will be required to remove the offending
  154. representation clause and recompile the program.  If your compiler
  155. does accept it, when you execute this program you will see that the
  156. type SMALL_INTEGER requires only 8 bits of storage.  After
  157. successfully compiling and running the program, comment out line
  158. 11 and compile and execute it again.  You will probably find that
  159. your compiler requires more than 8 bits to store data of this type.
  160. If your compiler cannot handle line 11, comment it out and compile
  161. and execute the resulting program.
  162.  
  163. Remember that at the beginning of this chapter we stated that of
  164. all the programs in this tutorial, this chapter would contain the
  165. ones that were most likely to have problems with your compiler.
  166. This is the reason that so many of these programs are not
  167. transportable from compiler to compiler.  Only three of the five
  168. compilers used to test these example programs implemented this
  169. particular clause.
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.                                                         Page 30-3
  177.  
  178.                           Chapter 30 - Machine Dependent Features
  179.  
  180. THE STORAGE_SIZE REPRESENTATION CLAUSE
  181. _________________________________________________________________
  182.  
  183. Another representation clause that is very similar to SIZE is the
  184. one named STORAGE_SIZE.  This is used to tell the compiler how many
  185. storage units to use to store an access type variable or a task
  186. type variable.  The LRM is not very specific on just what a storage
  187. unit is, so it must be defined by your compiler.  Because it is not
  188. well defined, and is therefore different for each compiler, an
  189. explanation may be more confusing than simply not attempting to
  190. explain it.  You will be left to study it on your own, remembering
  191. that it is similar to SIZE.  With all of the Ada you have studied
  192. to this point, you should be able to easily decipher the notes on
  193. this topic in your compiler documentation.
  194.  
  195.  
  196.  
  197. THE RECORD TYPE REPRESENTATION
  198. _________________________________________________________________
  199.  
  200. Examine the program named BITFIELD.ADA for an    ================
  201. example of two additional low level constructs,    BITFIELD.ADA
  202. the record type representation and the unchecked ================
  203. conversion.  We will begin with the record type
  204. representation.
  205.  
  206. First, we declare a record type named BITS with three fields of
  207. extremely limited range since we only wish to store one or two bits
  208. in each field.  Because of the limited range, we would like to
  209. instruct the compiler to store the individual variables in very
  210. small memory units, and in addition, we would like it to store all
  211. three fields in a single word.  We do this in lines 16 through 20
  212. where we give the compiler the desired pattern for the three
  213. fields.  It looks very much like a record except for the
  214. substitution of the reserved words for and use in place of type and
  215. is in the record type definition.  Remember that we are modifying
  216. the type we have already declared to tell the compiler how to
  217. actually implement it.
  218.  
  219. Each of the fields is slightly different also since the reserved
  220. word at is used followed by the number 0 in all three cases.  This
  221. is telling the system to store this variable in the word with an
  222. offset of zero from the first word, or in other words, we are
  223. telling the compiler to put this variable in the first word of the
  224. record.  Also, after the reserved word range, we have another
  225. defined range which tells the compiler which bits of the word to
  226. store these in.  The variable named Lower is therefore to be stored
  227. in the first word, and is to occupy bit positions 0 and 1 of that
  228. word.  The variable named Middle is also to be stored in the first
  229. word, and will occupy bit position 2 of that word.  The variable
  230. named High will occupy bit positions 3 and 4 of the same word.
  231.  
  232.  
  233.  
  234.  
  235.                                                         Page 30-4
  236.  
  237.                           Chapter 30 - Machine Dependent Features
  238.  
  239. WHAT DO THE BIT POSITIONS MEAN?
  240. _________________________________________________________________
  241.  
  242. By this point, you are probably wondering what is bit position 0.
  243. Is it the least significant bit or the most significant bit?  That
  244. question is entirely up to the compiler writer and you must consult
  245. your documentation for the answer to this question and nearly any
  246. other questions you may have about this new construct.  One
  247. possible resulting bit pattern is illustrated in figure 30-1.  The
  248. actual bit pattern for your compiler may be something entirely
  249. different.
  250.  
  251. The INTEGER type variable consists of a 16 bit number on nearly
  252. every microcomputer application and some minicomputers, but even
  253. this is up to the implementor to define in any way he desires.
  254. Because 16 bits is fairly standard for the INTEGER type variable,
  255. and for a single word, the various fields of the record were
  256. declared to be in one word to illustrate the next low level
  257. programming construct in Ada.  If your compiler is especially
  258. smart, you could continue the packing by telling the compiler to
  259. squeeze the entire record into as few as 5 bits, since that is all
  260. that would be needed to actually store the data.  This would be
  261. done using the SIZE representation clause in a manner similar to
  262. the last example program.
  263.  
  264.  
  265. THERE IS A LIMIT TO THE MODIFICATIONS
  266. _________________________________________________________________
  267.  
  268. After declaring the type, then modifying it to suit our purposes,
  269. we declare a variable of the new type in line 22.  Note that it
  270. would be an error to attempt to further modify the type after we
  271. have declared a variable of that type.  This is because it would
  272. allow declaring another variable of the newly modified type which
  273. would in fact be different from the first variable.
  274.  
  275. If additional fields were added with at 1 in their representation
  276. clauses, they would be put in the word that was at an offset of 1
  277. from the beginning of the record.  This would be the second word
  278. of course.  You can see that it is possible to very carefully
  279. control where and how the data is stored in a record of this data
  280. type.
  281.  
  282.  
  283. A PROBLEM GENERATOR, USE WITH CAUTION
  284. _________________________________________________________________
  285.  
  286. In line 25, we instantiate a copy of the generic function named
  287. Unchecked_Conversion to illustrate its use.  This is a function
  288. that can really get you into trouble, but can be a real time saver
  289. if you need its capability.  In this case, Switch_To_Bits will use
  290. an INTEGER for its source and a record of type BITS as the result
  291. or target.  A call to the function with an INTEGER type variable
  292. as an argument will change the type of the variable and return the
  293.  
  294.                                                         Page 30-5
  295.  
  296.                           Chapter 30 - Machine Dependent Features
  297.  
  298. same value with a new type.  In this case, because the individual
  299. bits are packed into a single word, the data in the INTEGER type
  300. variable is actually split up into the three fields of the record.
  301. The original data, as well as the three fields, are displayed for
  302. a small range of values.  In this case the composite data in the
  303. integer variable is unpacked into the respective fields by the
  304. system.
  305.  
  306. Note that line 37 could have used the loop index named Index as the
  307. actual parameter since it is legal in Ada to use a
  308. universal_integer in the call.
  309.  
  310. The only real requirement for use of the unchecked type conversion
  311. is that both structures have the same number of program units or
  312. bits.  The C programmer will recognize this as the union, and the
  313. Pascal programmer should see that this is the same as using a
  314. variant record for type conversion.
  315.  
  316. Be sure to compile and run this program to see if it really does
  317. what it should.  Your compiler may not implement some or all of
  318. these features, in which case you can only study the result of
  319. execution given at the end of the example program.  We said at the
  320. beginning of this chapter that there would be a few things you may
  321. not be able to do.  Only two of the five compilers tested compiled
  322. this program completely, and only one stored the bits in the
  323. pattern depicted in figure 30-1.  Note that the
  324. Unchecked_Conversion is not optional but required, and all five
  325. compilers tested by the author compiled it properly.
  326.  
  327.  
  328. THE PRAGMA NAMED PACK
  329. _________________________________________________________________
  330.  
  331. Examine the program named PACKITIN.ADA for an    ================
  332. example of use of the pragma named PACK.  This     PACKITIN.ADA
  333. is an instruction to the compiler to pack the    ================
  334. data as tightly as possible with no concern for
  335. how long it will take for the resulting program
  336. to execute.  Three examples of packing are given here, with each
  337. resulting in a more tightly packed composite type.
  338.  
  339.  
  340. NORMAL PACKING DENSITY
  341. _________________________________________________________________
  342.  
  343. Line 10 contains a declaration of a type which only requires 6 bits
  344. to store, but will probably require a full word of 16 bits or more
  345. on most implementations.  Lines 12 through 15 declare a record that
  346. may require 4 words because of alignment requirements in some
  347. compilers, and line 17 may even waste a few more words due to
  348. alignment considerations.  Lines 43 through 53 are used to output
  349. the sizes of these three types for study.  The results are given
  350. for two rather inexpensive Ada compilers used with MS-DOS, running
  351. on an IBM-PC type microcomputer.
  352.  
  353.                                                         Page 30-6
  354.  
  355.                           Chapter 30 - Machine Dependent Features
  356.  
  357.  
  358. SOME PACKING TAKES PLACE
  359. _________________________________________________________________
  360.  
  361. In line 19 the same type is repeated with a different name, and is
  362. used in the record in lines 21 through 25 where it is packed using
  363. the pragma PACK.  Note that the packing only takes place at the
  364. record level, not at the lower level which is assumed to be the
  365. default packing density.  In lines 27 and 28, the same array is
  366. declared with the pragma PACK used again at this level to achieve
  367. a better packing density than the previous example.  The results
  368. of execution illustrate that compiler 1 did a little better at
  369. packing than compiler 2 did.
  370.  
  371.  
  372. HIGHER PACKING DENSITY
  373. _________________________________________________________________
  374.  
  375. Lines 30 through 40 illustrate an even higher packing density
  376. because of the representation clause in line 31 where we instruct
  377. the compiler to use only 8 bits for the type used as elements in
  378. the composite types.  In this case, neither compiler supported the
  379. representation clause, so line 31 had to be commented out resulting
  380. in no additional packing density.
  381.  
  382.  
  383. WHICH COMPILER IS BEST?
  384. _________________________________________________________________
  385.  
  386. Simply because one compiler did a better packing job does not make
  387. it best between the two compared.  There is a penalty to be paid
  388. here when it comes to executing the code because the data fields
  389. are not located in exactly the same places for "normal" or unpacked
  390. data fields.  The compiler that packed the code very efficiently
  391. may take considerably longer to execute a program with data stored
  392. in this way than the other.  The important thing to remember is
  393. that the two compilers, even though both are validated, handled the
  394. types slightly differently.
  395.  
  396. Keep in mind that the pragma named PACK only packs the data at the
  397. level at which it is mentioned.  It does not pack the data at lower
  398. levels unless it is mentioned there also.  Three of the five
  399. compilers were able to compile this program completely and
  400. correctly.
  401.  
  402.  
  403. THE ENUMERATED TYPE REPRESENTATION
  404. _________________________________________________________________
  405.  
  406. The example program named ENUMREP.ADA             ===============
  407. illustrates the use of the representation clause    ENUMREP.ADA
  408. used with the enumerated type variable to define  ===============
  409. the values of the enumerated values.
  410.  
  411.  
  412.                                                         Page 30-7
  413.  
  414.                           Chapter 30 - Machine Dependent Features
  415.  
  416.  
  417. In this case we have declared a type named MOTION for use with some
  418. kind of a robot in which we wish to instruct the robot to move any
  419. one of four directions or stop.  A zero indicates a stopped
  420. condition, and the four directions are actually four different bits
  421. of a binary code.  Assuming that there is a different relay or
  422. electronic switch for each direction, we can output a single field
  423. to control the four relays or switches.  The important thing is
  424. that the enumerated value is the pattern we wish to output, so it
  425. can be output directly.
  426.  
  427. The enumerated type will work in exactly the same manner as any
  428. other enumerated type.  You can take the PRED, SUCC, VAL, or POS,
  429. and they will work the same way as if they were declared in order.
  430. We have only changed the underlying representation of the
  431. enumerated type.  The values must be declared in ascending order
  432. or a compile error will be issued.
  433.  
  434. Be sure to compile and execute this program to ascertain that it
  435. is acceptable to your compiler.  The enumerated representation
  436. clause is available with three of the five compilers tested.
  437.  
  438.  
  439.  
  440. THE ADDRESS SPECIFICATION
  441. _________________________________________________________________
  442.  
  443. The address specification is used in such a nebulous way that it
  444. is very difficult to even illustrate its use in a general purpose
  445. program, so a program is not provided.  The general form of its use
  446. is given in the next two lines which represents a fragment of a
  447. program.
  448.  
  449.      Robot_Port : MOTION;             -- The port to control
  450.                                       --  direction
  451.      for Robot_Port use at 16#0175#;  -- Absolute address of
  452.                                       --  the robot hardware
  453.  
  454. The first line of this sequence declares a variable named
  455. Robot_Port to be of type MOTION which was declared in the example
  456. program named ENUMREP.ADA.  You will recall that this type was
  457. intended to be used to control the robot's direction.  The second
  458. line tells the Ada compiler that this variable must be assigned the
  459. absolute address of 0175(hexadecimal), because this is where the
  460. output port is located which controls the robot's direction.  The
  461. reserved words for and use at tell the compiler where to locate
  462. this particular variable.
  463.  
  464. It should now be obvious why it is not practical to write a general
  465. purpose program to illustrate this concept.  The location of a
  466. usable port will be different on every computer, and the means of
  467. addressing the entire memory space can be quite complex in the case
  468. of segmented memory or with some sort of memory management scheme.
  469. Consult the documentation that came with your compiler to find the
  470.  
  471.                                                         Page 30-8
  472.  
  473.                           Chapter 30 - Machine Dependent Features
  474.  
  475. method used with your compiler to address an absolute memory
  476. location.  It will be listed in appendix F of your documentation,
  477. as required by the LRM.
  478.  
  479.  
  480.  
  481. UNCHECKED_DEALLOCATION
  482. _________________________________________________________________
  483.  
  484. This is also a very low level routine that is mentioned here for
  485. completeness.  Its use has been illustrated previously in this
  486. tutorial in chapters 13 and 23, where it was used to free up space
  487. that had been dynamically allocated.
  488.  
  489.  
  490.  
  491. PROGRAMMING EXERCISES
  492. _________________________________________________________________
  493.  
  494. 1.   Write a program using Unchecked_Conversion to convert an
  495.      INTEGER type variable into a CHARACTER array of as long as
  496.      needed to represent an INTEGER on your system, probably two
  497.      or four elements.  Use the ORD attribute to convert the
  498.      CHARACTER type data to numerical values and display the
  499.      numerical value of the components on the monitor.  This will
  500.      identify the underlying representation of the INTEGER and the
  501.      CHARACTER types.
  502.  
  503. 2.   Repeat exercise 1 to convert the FLOAT type to an array of
  504.      CHARACTER.
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.                                                         Page 30-9