home *** CD-ROM | disk | FTP | other *** search
/ Programming Tool Box / SIMS_2.iso / bp_6_93 / bonus / winer / chap2.txt < prev    next >
Text File  |  1994-09-03  |  105KB  |  1,920 lines

  1.                                  CHAPTER 2
  2.  
  3.                             VARIABLES AND DATA
  4.  
  5. DATA BASICS
  6. ===========
  7.  
  8. In Chapter 1 you examined the role of a compiler, and learned how it
  9. translates BASIC source code into the assembly language commands a PC
  10. requires.  But no matter how important the compiler is when creating a
  11. final executable program, it is only half of the story.  This chapter
  12. discusses the equally important other half: data.  Indeed, some form of
  13. data is integral to the operation of every useful program you will ever
  14. write.  Even a program that merely prints "Hello" to the display screen
  15. requires the data "Hello".
  16.      Data comes in many shapes and sizes, starting with a single bit,
  17. continuing through eight-byte double precision variables, and extending all
  18. the way to multi-megabyte disk files.  In this chapter you will learn about
  19. the many types of data that are available to you, and how they are
  20. manipulated in a BASIC program.  You will also learn how data is stored and
  21. assigned, and how BASIC's memory management routines operate.
  22.      Compiled BASIC supports two fundamental types of data (numeric and
  23. string), two primary methods of storage (static and dynamic), and two kinds
  24. of memory allocation (near and far).  Of course, the myriad of data types
  25. and methods is not present to confuse you.  Rather, each is appropriate in
  26. certain situations.  By fully understanding this complex subject, you will
  27. be able to write programs that operate as quickly as possible, and use the
  28. least amount of memory.
  29.      I will discuss each of the following types of data: integer and
  30. floating point numeric data, fixed-length and dynamic (variable-length)
  31. string data, and user-defined TYPE variables.  Besides variables which are
  32. identified by name, BASIC supports named constant data such as literal
  33. numbers and quoted strings.
  34.      I will also present a complete comparison of the memory storage
  35. methods used by BASIC, to compare near versus far storage, and dynamic
  36. versus static allocation.  It is important to understand that near storage
  37. refers to variables and other data that compete for the same 64K data space
  38. that is often referred to as Near Memory or Data Space.  By contrast, far
  39. storage refers to the remaining memory in a PC, up to the 640K limit that
  40. DOS imposes.  
  41.      The distinction between dynamic and static allocation is also
  42. important to establish now.  Dynamic data is allocated in whatever memory
  43. is available when a program runs, and it may be resized or erased as
  44. necessary.  Static data, on the other hand, is created by the compiler and
  45. placed directly into the .EXE file.  Therefore, the memory that holds
  46. static data may not be relinquished for other uses.
  47.      Each type of data has its advantages and disadvantages, as does each
  48. storage method.  To use an extreme example, you could store all numeric
  49. data in string variables if you really wanted to.  But this would require
  50. using STR$ every time a value was to be assigned, and VAL whenever a
  51. calculation had to be made.  Because STR$ and VAL are relatively slow,
  52. using strings this way will greatly reduce a program's performance. 
  53. Further, storing numbers as ASCII digits can also be very wasteful of
  54. memory.  That is, the double precision value 123456789.12345 requires
  55. fifteen bytes, as opposed to the usual eight.
  56.      Much of BASIC's broad appeal is that it lets you do pretty much
  57. anything you choose, using the style of programming you prefer.  But as the
  58. example above illustrates, selecting an appropriate data type can have a
  59. decided impact on a program's efficiency.  With that in mind, let's examine
  60. each kind of data that can be used with BASIC, beginning with integers.
  61.  
  62.  
  63. INTEGERS AND LONG INTEGERS
  64. ==========================
  65.  
  66. An integer is the smallest unit of numeric storage that BASIC supports, and
  67. it occupies two bytes of memory, or one "word".  Although various tricks
  68. can be used to store single bytes in a one-character string, the integer
  69. remains the most compact data type that can be directly manipulated as a
  70. numeric value.  Since the 80x86 microprocessor can operate on integers
  71. directly, using them in calculations will be faster and require less code
  72. than any other type of data.  An integer can hold any whole number within
  73. the range of -32768 to 32767 inclusive, and it should be used in all
  74. situations where that range is sufficient.  Indeed, the emphasis on using
  75. integers whenever possible will be a recurring theme throughout this book.
  76.      When the range of integer values is not adequate in a given
  77. programming situation, a long integer should be used.  Like the regular
  78. integer, long integers can accommodate whole numbers only.  A long integer,
  79. however, occupies four bytes of memory, and can thus hold more information. 
  80. This yields an allowable range of values that spans from -2147483648
  81. through 2147483647 (approximately +/- 2.15 billion).  Although the PC's
  82. processor cannot directly manipulate a long integer in most situations,
  83. calculations using them will still be much faster and require less code
  84. when compared to floating point numbers.
  85.      Regardless of which type of integer is being considered, the way they
  86. are stored in memory is very similar.  That is, each integer is comprised
  87. of either two or four bytes, and each of those bytes contains eight bits. 
  88. Since a bit can hold a value of either 0 or 1 only, you can see why a
  89. larger number of bits is needed to accommodate a wider range of values. 
  90. Two bits are required to count up to three, three bits to count to seven,
  91. four bits to count to fifteen, and so forth.
  92.      A single byte can hold any value between 0 and 255, however that same
  93. range can also be considered as spanning from -128 to 127.  Similarly, an
  94. integer value can hold numbers that range from either 0 to 65535 or -32768
  95. through 32767, depending on your perspective.  When the range is considered
  96. to be 0 to 65535 the values are referred to as *unsigned*, because only
  97. positive values may be represented.  BASIC does not, however, support
  98. unsigned integer values.  Therefore, that same range is used in BASIC
  99. programs to represent values between -32768 and 32767.  When integer
  100. numbers are considered as using this range they are called *signed*.
  101.      If you compile and run the short program in the listing that follows,
  102. the transition from positive to negative numbers will show how BASIC treats
  103. values that exceed the integer range of 32767.  Be sure not to use the /d
  104. debugging option, since that will cause an overflow error to be generated
  105. at the transition point.  The BASIC environment performs the same checking
  106. as /d does, and it too will report an error before this program can run to
  107. completion.
  108.  
  109.  
  110. Number% = 32760
  111. FOR X% = 1 TO 14
  112.     Number% = Number% + 1
  113.     PRINT Number%,
  114. NEXT
  115.  
  116. Displayed result:
  117.  
  118.  32761     32762     32763     32764     32765
  119.  32766     32767    -32768    -32767    -32766
  120. -32765    -32764    -32763    -32762    -32761
  121.  
  122.  
  123. As you can see, once an integer reaches 32767, adding 1 again causes the
  124. value to "wrap" around to -32768.  When Number% is further incremented its
  125. value continues to rise as expected, but in this case by becoming "less
  126. negative".  In order to appreciate why this happens you must understand how
  127. an integer is constructed from individual bits.  I am not going to belabor
  128. binary number theory or other esoteric material, and the brief discussion
  129. that follows is presented solely in the interest of completeness.
  130.  
  131.  
  132. BITS 'N' BYTES
  133. ==============
  134.  
  135. Sixteen bits are required to store an integer value.  These bits are
  136. numbered 0 through 15, and the least significant bit is bit number 0.  To
  137. help understand this terminology, consider the decimal number 1234.  Here,
  138. 4 is the least significant digit, because it contributes the least value to
  139. the entire number.  Similarly, 1 is the most significant portion, because
  140. it tells how many thousands there are, thus contributing the most to the
  141. total value.  The binary numbers that a PC uses are structured in an
  142. identical manner.  But instead of ones, tens, and hundreds, each binary
  143. digit represents the number of ones, twos, fours, eights, and so forth that
  144. comprise a given byte or word.
  145.      To represent the range of values between 0 and 32767 requires fifteen
  146. bits, as does the range from -32768 to -1.  When considered as signed
  147. numbers, the most significant bit is used to indicate which range is being
  148. considered.  This bit is therefore called the sign bit.  Long integers use
  149. the same method except that four bytes are used, so the sign bit is kept in
  150. the highest position of the fourth byte.
  151.      Selected portions of the successive range from 0 through -1 (or 65535)
  152. are shown in Table 2-1, to illustrate how binary counting operates.  When
  153. counting with decimal numbers, once you reach 9 the number is wrapped
  154. around to 0, and then a 1 is placed in the next column.  Since binary bits
  155. can count only to one, they wrap around much more frequently.  The
  156. Hexadecimal equivalents are also shown in the table, since they too are
  157. related to binary numbering.  That is, any Hex value whose most significant
  158. digit is 8 or higher is by definition negative.
  159.  
  160.  
  161.  Signed   Unsigned
  162. Decimal    Decimal          Binary          Hex
  163. ───────   ────────   ───────────────────   ────
  164.      0          0    0000 0000 0000 0000   0000
  165.      1          1    0000 0000 0000 0001   0001
  166.      2          2    0000 0000 0000 0010   0002
  167.      3          3    0000 0000 0000 0011   0003
  168.      4          4    0000 0000 0000 0100   0004
  169.      .          .                      .      .
  170.      .          .                      .      .
  171.  32765      32765    0111 1111 1111 1101   7FFD
  172.  32766      32767    0111 1111 1111 1110   7FFE
  173.  32767      32767    0111 1111 1111 1111   7FFF
  174. -32768      32768    1000 0000 0000 0000   8000
  175. -32767      32769    1000 0000 0000 0001   8001
  176. -32766      32770    1000 0000 0000 0010   8002
  177.      .          .                      .      .
  178.      .          .                      .      .
  179.     -4      65531    1111 1111 1111 1100   FFFB
  180.     -3      65532    1111 1111 1111 1101   FFFC
  181.     -2      65533    1111 1111 1111 1110   FFFD
  182.     -1      65534    1111 1111 1111 1111   FFFE
  183.      0      65535    0000 0000 0000 0000   FFFF
  184.  
  185. Table 2-1: When a signed integer is incremented past 32767, its value wraps
  186. around and becomes negative.
  187.  
  188.  
  189. MEMORY ADDRESSES AND POINTERS
  190. =============================
  191.  
  192. Before we can discuss such issues as variable and data storage, a few terms
  193. must be clarified.  A memory address is a numbered location in which a
  194. given piece of data is said to reside.  Addresses refer to places that
  195. exist in a PC's memory, and they are referenced by those numbers.  Every PC
  196. has thousands of memory addresses in which both data and code instructions
  197. may be stored.
  198.      A *pointer* is simply a variable that holds an address.  Consider a
  199. single precision variable named Value that has been stored by the compiler
  200. at memory address 10.  If another variable--let's call it Address%--is then
  201. assigned the value 10, Address% could be considered to be a pointer to
  202. Value.  Pointer variables are the bread and butter of languages such as C
  203. and assembler, because data is often read and written by referring to one
  204. variable which in turn holds the address of another variable.
  205.      Although BASIC shields you as the programmer from such details,
  206. pointers are in fact used internally by the BASIC language library
  207. routines.  This method of using pointers is sometimes called indirection,
  208. because an additional, indirect step is needed to first go to one variable,
  209. get an address, and then go to that address to access the actual data.  Now
  210. let's see how these memory issues affect a BASIC program.
  211.  
  212.  
  213. INTEGER STORAGE
  214. ===============
  215.  
  216. When a conventional two-byte integer is stored in the PC's memory, the
  217. lower byte is kept in the lower memory address.  For example, if X% is said
  218. to reside at address 10, then the least significant byte is at address 10
  219. and the most significant byte is at address 11.  Likewise, a long integer
  220. stored at address 102 actually occupies addresses 102 through 105, with the
  221. least significant portion at the lowest address.  This is shown graphically
  222. in Figure 2-1.
  223.  
  224.  
  225.    ┌───────────── X% ────────────┐
  226.    │     LSB             MSB     │
  227. ─ ╥─┬─┬─┬─┬─┬─┬─┬─╥─┬─┬─┬─┬─┬─┬─┬─╥── ─ ─
  228.   ║1│0│0│1│1│0│1│0║0│0│1│0│1│1│0│1║
  229. ─ ╨─┴─┴─┴─┴─┴─┴─┴─╨─┴─┴─┴─┴─┴─┴─┴─╨── ─ ─
  230.    ^               ^               ^
  231.    └─ Address 10   └─ Address 11   └─ Address 12
  232.  
  233. Figure 2-1: An integer is stored in two adjacent memory locations, with the
  234. Least Significant Byte at the lower address, and the Most Significant Byte
  235. at the higher.
  236.  
  237.  
  238. This arrangement certainly seems sensible, and it is.  However, some people
  239. get confused when looking at a range of memory addresses being displayed,
  240. because the values in lower addresses are listed at the left and the higher
  241. address values are shown on the right.  For example, the DEBUG utility that
  242. comes with DOS will display the Hex number ABCD as CD followed by AB.  I
  243. mention this only because the order in which digits are displayed will
  244. become important when we discuss advanced debugging in Chapter 4.
  245.      In case you are wondering, the compiler assigns addresses in the order
  246. in which variables are encountered.  The first address used is generally 36
  247. Hex, so in the program below the variables will be stored at addresses 36,
  248. 38, 3A, and then 3C.  Hex numbering is used for these examples because
  249. that's the way DEBUG and CodeView report them.
  250.  
  251.  
  252. A% = 1         'this is at address &H36
  253. B% = 2         'this is at address &H38
  254. C% = 3         'this is at address &H3A
  255. D% = 4         'this is at address &H3C
  256.  
  257.  
  258. FLOATING POINT VALUES
  259. =====================
  260.  
  261. Floating point variables and numbers are constructed in an entirely
  262. different manner than integers.  Where integers and long integers simply
  263. use the entire two or four bytes to hold a single binary number, floating
  264. point data is divided into portions.  The first portion is called the
  265. mantissa, and it holds the base value of the number.  The second portion is
  266. the exponent, and it indicates to what power the mantissa must be raised to
  267. express the complete value.  Like integers, a sign bit is used to show if
  268. the number is positive or negative.
  269.      The structure of single precision values in both IEEE and the original
  270. proprietary Microsoft Binary Format (MBF) is shown in Figure 2-2.  For IEEE
  271. numbers, the sign bit is in the most significant position, followed by
  272. eight exponent bits, which are in turn followed by 23 bits for the
  273. mantissa.  Double precision IEEE values are structured similarly, except
  274. eleven bits are used for the exponent and 52 for the mantissa.
  275.      Double precision MBF numbers use only eight bits for an exponent
  276. rather than eleven, trading a reduced absolute range for increased
  277. resolution.  That is, there are fewer exponent bits than the IEEE method
  278. uses, which means that extremely large and extremely small numbers cannot
  279. be represented.  However, the additional mantissa bits offer more absolute
  280. digits of precision.
  281.  
  282.  
  283. The IEEE format:
  284.  
  285. ┌────────┬────────┬────────┬────────┐
  286. │SEEEEEEE│EMMMMMMM│MMMMMMMM│MMMMMMMM│
  287. └────────┴────────┴────────┴────────┘
  288.  
  289. The MBF format:
  290.  
  291. ┌────────┬────────┬────────┬────────┐
  292. │EEEEEEEE│SMMMMMMM│MMMMMMMM│MMMMMMMM│
  293. └────────┴────────┴────────┴────────┘
  294.  
  295. Figure 2-2: A single precision value is comprised of a Sign bit, eight
  296. Exponent bits, and 23 bits to represent the Mantissa.  Each letter shown
  297. here represents one bit, and the bytes on the left are at higher addresses.
  298.  
  299.  
  300. Notice that with IEEE numbers, the exponent spans a byte boundary.  This
  301. undoubtedly contributes to the slow speed that results from using numbers
  302. in this format when a coprocessor is not present.  Contrast that with
  303. Microsoft's MBF format in which the sign bit is placed between the exponent
  304. and mantissa.  This allows direct access to the exponent with fewer
  305. assembler instructions, since the various bits don't have to be shifted
  306. around.
  307.      The IEEE format is used in QuickBASIC 4.0 and later, and BASIC PDS
  308. unless the /fpa option is used.  BASIC PDS uses the /fpa switch to specify
  309. an alternate math package which provides increased speed but with a
  310. slightly reduced accuracy.  Although the /fpa format is in fact newer than
  311. the original MBF used in interpreted BASIC and QuickBASIC 2 and 3, it is
  312. not quite as fast.
  313.      As was already mentioned, double precision data requires twice as many
  314. bytes as single precision.  Further, due to the inherent complexity of the
  315. way floating point data is stored, an enormous amount of assembly language
  316. code is required to manipulate it.  Common sense therefore indicates that
  317. you would use single precision variables whenever possible, and reserve
  318. double precision only for those cases where the added accuracy is truly
  319. necessary.  Using either floating point variable type, however, is still
  320. very much slower than using integers and long integers.  Worse, rounding
  321. errors are inevitable with any floating point method, as the following
  322. short program fragment illustrates.
  323.  
  324.  
  325. FOR X% = 1 TO 10000
  326.     Number! = Number! + 1.1
  327. NEXT
  328. PRINT Number!
  329.  
  330. Displayed result:
  331.  
  332. 10999.52
  333.  
  334.  
  335. Although the correct answer should be 11000, the result of adding 1.1 ten
  336. thousand times is incorrect by a small amount.  If you are writing a
  337. program that computes, say, tax returns, even this small error will be
  338. unacceptable.  Recognizing this problem, Microsoft developed a new Currency
  339. data type which was introduced with BASIC PDS version 7.0.
  340.      The Currency data type is a cross between an integer and a floating
  341. point number.  Like a double precision value, Currency data also uses eight
  342. bytes for storage.  However, the numbers are stored in an integer format
  343. with an implied scaling of 10000.  That is, a binary value of 1 is used to
  344. represent the value .0001, and a binary value of 20000 is treated as a 2. 
  345. This yields an absolute accuracy to four decimal places, which is more than
  346. sufficient for financial work.  The absolute range of Currency data is plus
  347. or minus 9.22 times 10 ^ 14 (± 9.22E14 or 922,000,000,000,000.0000), which
  348. is very wide indeed.  This type of storage is called Fixed-Point, because
  349. the number of decimal places is fixed (in this case at four places).
  350.      Currency data offers the best compromise of all, since only whole
  351. numbers are represented and the fractional portion is implied.  Further,
  352. since a separate exponent and mantissa are not used, calculations involving
  353. Currency data are extremely fast.  In practice, a loop that adds a series
  354. of Currency variables will run about half as fast as the same loop using
  355. long integers.  Since twice as many bytes must be manipulated, the net
  356. effect is an overall efficiency that is comparable to long integers. 
  357. Compare that to double precision calculations, where manipulating the same
  358. eight bytes takes more than six times longer.
  359.      As you have seen, there is a great deal more to "simple" numeric data
  360. than would appear initially.  But this hardly begins to scratch the surface
  361. of data storage and manipulation in BASIC.  We will continue our tour of
  362. BASIC's data types with conventional dynamic (variable-length) strings,
  363. before proceeding to fixed-length strings and TYPE variables.
  364.  
  365.  
  366. DYNAMIC STRINGS
  367. ===============
  368.  
  369. One of the most important advantages that BASIC holds over all of the other
  370. popular high-level languages is its support for dynamic string data.  In
  371. Pascal, for example, you must declare every string that your program will
  372. use, as well as its length, before the program can be compiled.  If you
  373. determine during execution of the program that additional characters must
  374. be stored in a string, you're out of luck.
  375.      Likewise, strings in C are treated internally as an array of single
  376. character bytes, and there is no graceful way to extend or shorten them. 
  377. Specifying more characters than necessary will of course waste memory, and
  378. specifying too few will cause subsequent data to be overwritten.  Since C
  379. performs virtually no error checking during program execution, assigning to
  380. a string that is not long enough will corrupt memory.  And indeed, problems
  381. such as this cause untold grief for C programmers.
  382.      Dynamic string memory handling is built into BASIC, and those routines
  383. are written in assembly language.  BASIC is therefore extremely efficient
  384. and very fast in this regard.  Since C is a high-level language, writing an
  385. equivalent memory manager in C would be quite slow and bulky by comparison. 
  386. I feel it is important to point out BASIC's superiority over C in this
  387. regard, because C has an undeserved reputation for being a very fast and
  388. powerful language.
  389.      Compiled BASIC implements dynamic strings with varying lengths by
  390. maintaining a *string descriptor* for each string.  A string descriptor is
  391. simply a four-byte table that holds the current length of the string as
  392. well as its current address.  The format for a BASIC string descriptor is
  393. shown in Figure 2-3.  In QuickBASIC programs and BASIC PDS when far strings
  394. are not specified, all strings are stored in an area of memory called the
  395. *near heap*.  The string data in this memory area is frequently shuffled
  396. around, as new strings are assigned and old ones are abandoned.
  397.  
  398.  
  399. ┌──────┐   Higher addresses
  400. │  64  │           ^
  401. ├──────┤ Address   │
  402. │  B2  │           │
  403. ╞══════╡           │
  404. │  00  │           │
  405. ├──────┤ Length    
  406. │  0A  │ ■──────────────── VARPTR(Work$)
  407. └──────┘
  408.  
  409. Figure 2-3: Each string in a QuickBASIC program has a corresponding string
  410. descriptor, which holds its current length and address.  The string in this
  411. example has a length of ten characters (0A Hex) and its data is presently
  412. at address 25778 (64B2 Hex).
  413.  
  414.  
  415. The lower two bytes in a string descriptor together hold the current length
  416. of the string, and the second two bytes hold its address.  The memory
  417. location at the bottom of Figure 2-3 is at the lowest address.  The short
  418. program below shows how you could access a string by peeking at its
  419. descriptor.
  420.  
  421.  
  422. DEFINT A-Z
  423.  
  424. Test$ = "BASIC Techniques and Utilities"
  425. Descr = VARPTR(Test$)
  426. Length = PEEK(Descr) + 256 * PEEK(Descr + 1)
  427. Addr = PEEK(Descr + 2) + 256 * PEEK(Descr + 3)
  428.  
  429. PRINT "The length is"; Length
  430. PRINT "The address is"; Addr
  431. PRINT "The string contains ";
  432. FOR X = Addr TO Addr + Length - 1
  433.   PRINT CHR$(PEEK(X));
  434. NEXT
  435.  
  436. Displayed result:
  437.  
  438. The length is 17
  439. The address is 15646 (this will vary)
  440. The string contains BASIC Techniques and Utilities
  441.  
  442.  
  443. Each time a string is assigned or reassigned, memory in the heap is claimed
  444. and the string's descriptor is updated to reflect its new length and
  445. address.  The old data is then marked as being abandoned, so the space it
  446. occupied may be reclaimed later on if it is needed.  Since each assignment
  447. claims new memory, at some point the heap will become full.  When this
  448. happens, BASIC shuffles all of the string data that is currently in use
  449. downward on top of the older, abandoned data.  This heap compaction process
  450. is often referred to colorfully as *garbage collection*.
  451.      In practice, there are two ways to avoid having BASIC claim new space
  452. for each string assignment--which takes time--and you should consider these
  453. when speed is paramount.  One method is to use LSET or RSET, to insert new
  454. characters into an existing string.  Although this cannot be used to make a
  455. string longer or shorter, it is very much faster than a straight assignment
  456. which invokes the memory management routines.  The second method is to use
  457. the statement form of MID$, which is not quite as fast as LSET, but is more
  458. flexible.
  459.      Microsoft BASIC performs some additional trickery as it manages the
  460. string data in a program.  For example, whenever a string is assigned, an
  461. even number of bytes is always requested.  Thus, if a five-character string
  462. is reassigned to one with six characters, the same space can be reused. 
  463. Since claiming new memory requires a finite amount of time and also causes
  464. garbage collection periodically, this technique helps to speed up the
  465. string assignment process.
  466.      For example, in a program that builds a string by adding new
  467. characters to the end in a loop, BASIC can reduce the number of times it
  468. must claim new memory to only every other assignment.  Another advantage to
  469. always allocating an even number of bytes is that the 80286 and later
  470. microprocessors can copy two-byte words much faster than they can copy the
  471. equivalent number of bytes.  This has an obvious advantage when long
  472. strings are being assigned.
  473.      In most cases, BASIC's use of string descriptors is much more
  474. efficient than the method used by C and other languages.  In C, each string
  475. has an extra trailing CHR$(0) byte just to mark where it ends.  While using
  476. a single byte is less wasteful than requiring a four-byte table, BASIC's
  477. method is many times faster.  In C the entire string must be searched just
  478. to see how long it is, which takes time.  Likewise, comparing and
  479. concatenating strings in C requires scanning both strings for the
  480. terminating zero character.  The same operations in BASIC require but a
  481. single step to obtain the current length.
  482.      Pascal uses a method that is similar to BASIC's, in that it remembers
  483. the current length of the string. The length is stored with the actual
  484. string data, in a byte just before the first character.  Unfortunately,
  485. using a single byte limits the maximum length of a Pascal string to only
  486. 255 characters.  And again, when a string is shortened in Pascal, the extra
  487. characters are not released for use by other data.  But it is only fair to
  488. point out that Pascal's method is both fast and compact.  And since strings
  489. in C and Pascal never move around in memory, garbage collection is not
  490. required.
  491.      Although a BASIC string descriptor uses four bytes of additional
  492. memory beyond that needed for the actual data, this is only part of the
  493. story.  An additional two bytes are needed to hold a special "variable"
  494. called a *back pointer*.  A back pointer is an integer word that is stored
  495. in memory immediately before the actual string data, and it holds the
  496. address of the data's string descriptor.  Thus, it is called a back pointer
  497. because it points back to the descriptor, as opposed to the descriptor
  498. which points to the data.
  499.      Because of this back pointer, six additional bytes are actually needed
  500. to store each string, beyond the number of characters that it contains. 
  501. For example, the statement Work$ = "BASIC" requires twelve bytes of data
  502. memory--five for the string itself, one more because an even number of
  503. bytes is always claimed, four for the descriptor, and two more for a back
  504. pointer.  Every string that is defined in a program has a corresponding
  505. descriptor which is always present, however a back pointer is maintained
  506. only while the string has characters assigned to it.  Therefore, when a
  507. string is erased the two bytes for its back pointer are also relinquished.
  508.      I won't belabor this discussion of back pointers further, because
  509. understanding them is of little practical use.  Suffice it to say that a
  510. back pointer helps speed up the heap compaction process.  Since the address
  511. portion of the descriptor must be updated whenever the string data is
  512. moved, this pointer provides a fast link between the data being moved and
  513. its descriptor.  By the way, the term "pointer" refers to any variable that
  514. holds a memory address, regardless of what language is being considered.
  515.  
  516.  
  517. FAR STRINGS IN BASIC PDS
  518.  
  519. BASIC PDS offers an option to specify "far strings", whereby the string
  520. data is not stored in the same 64K memory area that holds most of a
  521. program's data.  The method of storage used for far strings is of necessity
  522. much more complex than near strings, because both an address and a segment
  523. must be kept track of.  Although Microsoft has made it clear that the
  524. structure of far string descriptors may change in the future, I would be
  525. remiss if this undocumented information were not revealed here.  The
  526. following description is valid as of BASIC 7.1 [it is still valid for
  527. VB/DOS too].
  528.      For each far string in a program, a four-byte descriptor is maintained
  529. in near memory.  The lower two bytes of the descriptor together hold the
  530. address of an integer variable that holds yet another address: that of the
  531. string length and data.  The second pair of bytes also holds the address of
  532. a pointer, in this case a pointer to a variable that indicates the segment
  533. in which the string data resides.  Thus, by retrieving the address and
  534. segment from the descriptor, you can locate the string's length and data,
  535. albeit with an extra level of indirection.
  536.      It is interesting to note that when far strings are being used, the
  537. string's length is kept just before its data, much like the way Pascal
  538. operates.  Therefore, the address pointer holds the address of the length
  539. word which immediately precedes the actual string data.
  540.      The short program that follows shows how to locate all of the
  541. components of a far string based on examining its descriptor and related
  542. pointers.  Notice that long integers are used to avoid the possibility of
  543. an overflow error if the segment or addresses happen to be higher than
  544. 32767.  This way you can run the program in the QBX [or VB/DOS] editing
  545. environment.  Figure 2-4 in turn illustrates the relationship between the
  546. address and pointer information graphically.
  547.  
  548.  
  549. DEF FNPeekWord& (A&)
  550.   FNPeekWord& = PEEK(A&) + 256& * PEEK(A& + 1)
  551. END DEF
  552.  
  553. Work$ = "This is a test"
  554.  
  555. DescAddr& = VARPTR(Work$)
  556. AddressPtr& = FNPeekWord&(DescAddr&)
  557. SegmentPtr& = FNPeekWord&(DescAddr& + 2)
  558. Segment& = FNPeekWord&(SegmentPtr&)
  559.  
  560. DEF SEG = Segment&
  561. DataAddr& = FNPeekWord&(AddressPtr&)
  562. Length% = FNPeekWord&(DataAddr&)
  563. StrAddr& = DataAddr& + 2
  564.  
  565. PRINT "The descriptor address is:"; DescAddr&
  566. PRINT "      The data segment is:"; Segment&
  567. PRINT "            The length is:"; Length%
  568. PRINT "The string data starts at:"; StrAddr&
  569. PRINT "   And the string data is: ";
  570.  
  571. FOR X& = StrAddr& TO StrAddr& + Length% - 1
  572.   PRINT CHR$(PEEK(X&));
  573. NEXT
  574.  
  575. Displayed result (the addresses may vary):
  576.  
  577. The descriptor address is: 17220
  578.       The data segment is: 40787
  579.             The length is: 14
  580. The string data starts at: 106
  581.    And the string data is: This is a test
  582.  
  583.  
  584. Because two bytes are used to hold the segment, address, and length values,
  585. we must PEEK both of them and combine the results.  This is the purpose of
  586. the PeekWord function that is defined at the start of the program.  Note
  587. the placement of an ampersand after the number 256, which ensures that the
  588. multiplication will not cause an overflow error.  I will discuss such use
  589. of numeric constants and type identifiers later in this chapter.
  590.  
  591.  
  592.      ┌───┬────────────────── The string length
  593.      │   │      ┌─────────── The string data
  594.     ┌┴─┬─┴┬─────┴────────┐
  595. ┌──>│0A│00│This is a test│<── Segment &H8F00
  596. │   └──┴──┴──────────────┘
  597. │    ^
  598. │    └─────── 8F00:0070
  599. │   ┌──┬──┬──────────────┐
  600. └───┤70│00│..............│<── Segment &H8F00
  601.     └──┴──┴──────────────┘
  602.      ^
  603.      ├─────── 8F00:002E
  604.      │
  605.      │
  606.      │ This is the "near" segment ──────┐
  607.      └───────────────┬───┐              │
  608.     ┌──┬──┬─────────┬┴─┬─┴┬──┬──┬───────┴────┐
  609.     │00│8F│.........│2E│00│D4│03│............│
  610.     └──┴──┴─────────┴──┴──┴┬─┴─┬┴────────────┘
  611.      ^               ^     └─┬─┘
  612.      ├─ Address 03D4 │       │
  613.      │               └───────┼── VARPTR(Work$)
  614.      └───────────────────────┘
  615.  
  616. Figure 2-4: A far string descriptor holds the addresses of other addresses,
  617. in this case addresses that hold a far string's segment and its length and
  618. actual data.
  619.  
  620.  
  621. Even in a far-string program, some of the string data will be near.  For
  622. example, DATA items and quoted string constants are stored in the same 64K
  623. DGROUP data segment that holds simple numeric and TYPE variables.  The same
  624. "indirect" method is used, whereby you must look in one place to get the
  625. address of another address.  In this case, however, the "far" segment that
  626. is reported is simply the normal near data segment.  [DATA items in VB/DOS
  627. programs are still kept in near memory, but quoted strings are now kept in
  628. a separate segment.]
  629.      One final complication worth mentioning is that strings within a FIELD
  630. buffer (and possibly in other special situations) are handled slightly
  631. differently.  Since all of the strings in a FIELD buffer must be
  632. contiguous, BASIC cannot store the length word adjacent to the string data. 
  633. Therefore, a different method must be used.  This case is indicated by
  634. setting the sign bit (the highest bit) in the length word as a flag.  Since
  635. no string can have a negative length, that bit can safely be used for this
  636. purpose.  When a string is stored using this alternate method, the bytes
  637. that follow the length word are used as additional pointers to the string's
  638. actual data segment and address.
  639.  
  640.  
  641. FIXED-LENGTH STRINGS
  642.  
  643. One of the most important new features Microsoft added beginning with
  644. QuickBASIC 4.0 was fixed-length string and TYPE variables.  Although fixed-
  645. length strings are less flexible than conventional BASIC strings, they
  646. offer many advantages in certain programming situations.  One advantage is
  647. that they are static, which means their data does not move around in memory
  648. as with conventional strings.  You can therefore obtain the address of a
  649. fixed-length string just once using VARPTR, confident that this address
  650. will never change.  With dynamic strings, SADD must be used each time the
  651. address is needed, which takes time and adds code.  Another important
  652. feature is that arrays of fixed-length strings can be stored in far memory,
  653. outside of the normal 64K data area.  We will discuss near and far array
  654. memory allocation momentarily.
  655.      With every advantage, however, comes a disadvantage.  The most severe
  656. limitation is that when a fixed-length string is used where a conventional
  657. string is expected, BASIC must generate code to create a temporary dynamic
  658. string, and then copy the data to it.  That is, all of BASIC's internal
  659. routines that operate on strings expect a string descriptor.  Therefore,
  660. when you print a fixed-length string, or use MID$ or INSTR or indeed nearly
  661. any statement or function that accepts a string, it must be copied to a
  662. form that BASIC's internal routines can accept.  In many cases, additional
  663. code is created to delete the temporary string afterward.  In others, the
  664. data remains until the next time the same BASIC statement is executed, and
  665. a new temporary string is assigned freeing the older one.
  666.      To illustrate, twenty bytes of assembly language code are required to
  667. print a fixed-length string, compared to only nine for a conventional
  668. dynamic string.  Worse, when a fixed-length string is passed as an argument
  669. to a subprogram or function, BASIC not only makes a copy before passing the
  670. string, but it also copies the data back again in case the subroutine
  671. changed it!  The extra steps the compiler performs are shown as BASIC
  672. equivalents in the listing that follows.
  673.  
  674.  
  675. '----- This is the code you write:
  676.  
  677. DIM Work AS STRING * 20
  678. CALL TestSub(Work$)
  679.  
  680. '----- This is what BASIC actually does:
  681.  
  682. Temp$ = SPACE$(20)    'create a temporary string
  683. LSET Temp$ = Work$    'copy Work$ to it
  684. CALL TestSub(Temp$)   'call the subprogram
  685. LSET Work$ = Temp$    'copy the data back again
  686. Temp$ = ""            'erase the temporary data
  687.  
  688.  
  689. As you can imagine, all of this copying creates an enormous amount of
  690. additional code in your programs.  Where only nine bytes are required to
  691. pass a conventional string to a subprogram, 64 are needed when a fixed-
  692. length string is being sent.  But you cannot assume unequivocally that
  693. conventional strings are always better or that fixed-length strings are
  694. always better.  Rather, I can only present the facts, and let you decide
  695. based on the knowledge of what is really happening.  In the discussion of
  696. debugging later in Chapter 4, you will learn how to use CodeView to see the
  697. code that BASIC generates.  You can thus explore these issues further, and
  698. draw your own conclusions.
  699.  
  700.  
  701. USER-DEFINED TYPE VARIABLES
  702.  
  703. As I mentioned earlier, the TYPE variable is an important and powerful
  704. addition to modern compiled BASIC.  Its primary purpose is to let
  705. programmers create composite data structures using any combination of
  706. native data types.  C and Pascal have had such user-defined data types
  707. since their inception, and they are called Structures and Records
  708. respectively in each language.
  709.      One immediately obvious use for being able to create a new, composite
  710. data type is to define the structure of a random access data file.  Another
  711. is to simulate an array comprised of varied types of data.  Obviously, no
  712. language can support a mix of different data types within a single array. 
  713. That is, an array cannot be created where some of the elements are, say,
  714. integer while others are double precision.  But a TYPE variable lets you do
  715. something very close to that, and you can even create arrays of TYPE
  716. variables.
  717.      In the listing that follows a TYPE is defined using a mix of integer,
  718. single precision, double precision, and fixed-length string components. 
  719. Also shown below is how a TYPE variable is dimensioned, and how each of its
  720. components are assigned and referenced.
  721.  
  722.  
  723. TYPE MyType
  724.   I AS INTEGER
  725.   S AS SINGLE
  726.   D AS DOUBLE
  727.   F AS STRING * 20
  728. END TYPE
  729.  
  730. DIM MyData as MyType
  731.  
  732. MyData.I = 12       'assign the integer portion
  733. MyData.S = 100.09   'and then the single part
  734. MyData.D = 43.2E56  'and then the double
  735. MyData.F = "Test"   'and finally the string
  736.  
  737. PRINT MyData.F      'now print the string
  738.  
  739.  
  740. Once the TYPE structure has been established, the DIM statement must be
  741. used to create an actual variable using that arrangement.  Although DIM is
  742. usually associated with the definition of arrays, it is also used to
  743. identify a variable name with a particular type of data.  In this case, DIM
  744. tells BASIC to set aside an area of memory to hold that many bytes.  You
  745. may also use DIM with conventional variable types.  For example, DIM
  746. LastName AS STRING or DIM PayPeriod AS DOUBLE lets you omit the dollar sign
  747. and pound sign when you reference them later in the program.  In my
  748. opinion, however, that style leads to programs that are difficult to
  749. maintain, since many pages later in the source listing you may not remember
  750. what type of data is actually being referred to.
  751.      As you can see, a period is needed to indicate which portion of the
  752. TYPE variable is being referenced.  The base name is that given when you
  753. dimensioned the variable, but the portion being referenced is identified
  754. using the name within the original TYPE definition.  You cannot print a
  755. TYPE variable directly, but must instead print each component separately. 
  756. Likewise, assignments to a TYPE variable must also be made through its
  757. individual components, with two exceptions.  You may assign an entire TYPE
  758. variable from another identical TYPE directly, or from a dissimilar TYPE
  759. variable using LSET.
  760.      For example, if we had used DIM MyData AS MyType and then DIM HisData
  761. AS MyType, the entire contents of HisData could be assigned to MyData using
  762. the statement MyData = HisData.  Had HisData been dimensioned using a
  763. different TYPE definition, then LSET would be required.  That is, LSET
  764. MyData = HisData will copy as many characters from HisData as will fit into
  765. MyData, and then pad the remainder, if any, with blanks.
  766.      It is important to understand that this behavior can cause strange
  767. results indeed.  Since CHR$(32) blanks are used to pad what remains in the
  768. TYPE variable being assigned, numeric components may receive some unusual
  769. values.  Therefore, you should assign differing TYPE variables only when
  770. those overlapping portions being assigned are structured identically.
  771.  
  772.  
  773. Arrays Within Types
  774.  
  775. With the introduction of BASIC PDS, programmers may also establish static
  776. arrays within a single TYPE definition.  An array is dimensioned within a
  777. TYPE as shown in the listing that follows.  As with a conventional DIM
  778. statement for an array, the number of elements are indicated and a non-zero
  779. lower bound may optionally be specified.  Please understand, though, that
  780. you cannot use a variable for the number of elements in the array.  That
  781. is, using PayHistory(1 TO NumDates) would be illegal.
  782.  
  783.  
  784. TYPE ArrayType
  785.   AmountDue AS SINGLE
  786.   PayHistory(1 TO 52) AS SINGLE
  787.   LastName AS STRING * 15
  788. END TYPE
  789.  
  790. DIM TypeArray AS ArrayType
  791.  
  792.  
  793. There are several advantages to using an array within a TYPE variable.  One
  794. is that you can reference a portion of the TYPE by using a variable to
  795. specify the element number.  For example, TypeArray.PayHistory(PayPeriod) =
  796. 344.95 will assign the value 344.95 to element number PayPeriod.  Without
  797. the ability to use an array, each of the 52 components would need to be
  798. identified by name.  Further, arrays allows you to define a large number of
  799. TYPE elements with a single program statement.  This can help to improve a
  800. program's readability.
  801.  
  802.  
  803. STATIC VS. DYNAMIC DATA
  804. =======================
  805.  
  806. Preceding sections have touched only briefly on the concept of static and
  807. dynamic memory storage.  Let's now explore this subject in depth, and learn
  808. which methods are most appropriate in which situations.
  809.      By definition, static data is that which never changes in size, and
  810. never moves around in memory.  In compiled BASIC this definition is further
  811. extended to mean all data that is stored in the 64K near memory area known
  812. as DGROUP.  This includes all numeric variables, fixed-length strings, and
  813. TYPE variables.  Technically speaking, the string descriptors that
  814. accompany each conventional (not fixed-length) string are also considered
  815. to be static, even though the string data itself is not.  The string
  816. descriptors that comprise a dynamic string array, however, are dynamic
  817. data, because they move around in memory (as a group) and may be resized
  818. and erased.
  819.      Numeric arrays that are dimensioned with constant (not variable)
  820. subscripts are also static, unless the '$DYNAMIC metacommand has been used
  821. in a preceding program statement.  That is, DIM Array#(0 TO 100) will
  822. create a static array, while DIM Array#(0 TO MaxElements) creates a dynamic
  823. array.  Likewise, arrays of fixed-length strings and TYPE variables will be
  824. static, as long as numbers are used to specify the size.
  825.      There are advantages and disadvantages to each storage method.  Access
  826. to static data is always faster than access to dynamic data, because the
  827. compiler knows the address where the data resides at the time it creates
  828. your program.  It can therefore create assembly language instructions that
  829. go directly to that address.  In contrast, dynamic data always requires a
  830. pointer to hold the current address of the data.  An extra step is
  831. therefore needed to first get the data address from that pointer, before
  832. access to the actual data is possible.  Static data is also in the near
  833. data segment, thus avoiding the need for additional code that switches
  834. segments.
  835.      The overwhelming disadvantage of static data, though, is that it may
  836. never be erased.  Once a static variable or array has been used in a
  837. program, the memory it occupies can never be released for other uses. 
  838. Again, it is impossible to state that static arrays are always better than
  839. dynamic arrays or vice versa.  Which you use must be dictated by your
  840. program's memory requirements, when compared to its execution speed.
  841.  
  842.  
  843. DYNAMIC ARRAYS
  844.  
  845. You have already seen how dynamic strings operate, by using a four-byte
  846. pointer table called a string descriptor.  Similarly, a dynamic array also
  847. needs a table to show where the array data is located, how many elements
  848. there are, the length of each element, and so forth.  This table is called
  849. an array descriptor, and it is structured as shown in Table 2-2.
  850.      There is little reason to use the information in an array descriptor
  851. in a BASIC program, and indeed, BASIC provides no direct way to access it
  852. anyway.  But when writing routines in assembly language for use with BASIC,
  853. this knowledge can be quite helpful.  As with BASIC PDS far string
  854. descriptors, none of this information is documented, and relying on it is
  855. most certainly not endorsed by Microsoft.  Perhaps that's what makes it so
  856. much fun to discuss!
  857.      Technically speaking, only dynamic arrays require an array descriptor,
  858. since static arrays do not move or change size.  But BASIC creates an array
  859. descriptor for every array, so only one method of code generation is
  860. necessary.  For example, when you pass an entire array to a subprogram
  861. using empty parentheses, it is the address of the array descriptor that is
  862. actually sent.  The subprogram can then access the data through that
  863. descriptor, regardless of whether the array is static or dynamic.
  864.  
  865.  
  866. Offset  Size        Description
  867. ──────  ────  ──────────────────────────────────
  868.   00     02   Address where array data begins
  869.  
  870.   00     02   Segment where that address resides
  871.  
  872.   04     02   Far heap descriptor, pointer
  873.  
  874.   06     02   Far heap descriptor, block size 
  875.  
  876.   08     01   Number of dimensions in the array
  877.  
  878.   09     01   Array type and storage method:
  879.                 Bit 0 set = far array
  880.                 Bit 1 set = huge (/ah) array
  881.                 Bit 6 set = static array
  882.                 Bit 7 set = string array
  883.  
  884.   0A     02   Adjusted Offset 
  885.  
  886.   0C     02   Length in bytes of each element
  887.  
  888.   0E     02   Number of elements in the last
  889.               dimension (UBOUND - LBOUND + 1)
  890.  
  891.   10     02   First element number in that 
  892.               dimension (LBOUND)
  893.  
  894.   12     02   Number of elements in the second
  895.               from last dimension
  896.  
  897.   14     02   First element number in that
  898.               dimension
  899.  
  900.     .    02   Repeat number of elements and
  901.               first element number as necessary,
  902.     .    02   through the first dimension
  903.  
  904. Table 2-2: Every array in a BASIC program has an associated array
  905. descriptor such as the one shown here.  This descriptor contains important
  906. information about the array.
  907.  
  908.  
  909. The first four bytes together hold the segmented address where the array
  910. data proper begins in memory.  Following the standard convention, the
  911. address is stored in the lower word, with the segment immediately
  912. following.
  913.      The next two words comprise the Far Heap Descriptor, which holds a
  914. pointer to the next dynamic array descriptor and the current size of the
  915. array.  For static arrays both of these entries are zero.  When multiple
  916. dynamic arrays are used in a program, the array descriptors are created in
  917. static DGROUP memory in the order BC encounters them.  The Far Heap Pointer
  918. in the first array therefore points to the next array descriptor in memory. 
  919. The last descriptor in the chain can be identified because it points to a
  920. word that holds a value of zero.
  921.      The block size portion of the Far Heap Descriptor holds the size of
  922. the array, using a byte count for string arrays and a "paragraph" count for
  923. numeric, fixed-length, and TYPE arrays.  For string arrays--whether near or
  924. far--the byte count is based on the four bytes that each descriptor
  925. occupies.  With numeric arrays the size is instead the number of 16-byte
  926. paragraphs that are needed to store the array.
  927.      The next entry is a single byte that holds the number of dimensions in
  928. the array.  That is, DIM Array(1 TO 10) has one dimension and DIM Array(1
  929. TO 10, 2 TO 20) has two.
  930.      The next item is also a byte, and it is called the Feature byte
  931. because the various bits it holds tell what type of array it is.  As shown
  932. in the table, separate bits are used to indicate if the array is stored in
  933. far memory, whether or not /ah was used to specify huge arrays, if the
  934. array is static, and if it is a string array.  Multiple bits are used for
  935. each of these array properties, since they may be active in combination. 
  936. However, BASIC never sets the far and huge bits for string arrays, even
  937. when the PDS /fs option is used and the strings are in fact in far memory.
  938.      Of particular interest is the Adjusted Offset entry.  Even though the
  939. segmented address where the array data begins is the first entry in the
  940. descriptor, it is useful only when the first element number in the array is
  941. zero.  This would be the case with DIM Array(0 TO N), or simply DIM
  942. Array(N).  To achieve the fastest performance possible when retrieving or
  943. assigning a given element, the Adjusted Offset is calculated when the array
  944. is dimensioned to compensate for an LBOUND other than 0.
  945.      For example, if an integer array is dimensioned starting at element 1,
  946. the Adjusted Offset is set to point two bytes before the actual starting
  947. address of the data.  This way, the compiler can take the specified element
  948. number, multiply that times two (each element comprises two bytes), and
  949. then add that to the Adjusted Offset to immediately point at the correct
  950. element in memory.  Otherwise, additional code would be needed to subtract
  951. the LBOUND value each time the array is accessed.  Since the array's LBOUND
  952. is simply constant information, it would be wasteful to calculate that
  953. repeatedly at run time.  Of course, the Adjusted Offset calculation is
  954. correspondingly more complex when dealing with multi-dimensional arrays.
  955.      The remaining entries identify the length of each element in bytes,
  956. and the upper and lower bounds.  String arrays always have a 4 in the
  957. length location, because that's the length of each string descriptor.  A
  958. separate pair of words is needed for each array subscript, to identify the
  959. LBOUND value and the number of elements.  The UBOUND is not actually stored
  960. in the array descriptor, since it can be calculated very easily when
  961. needed.  Notice that for multi-dimensional arrays, the last (right-most)
  962. subscript is identified first, followed by the second from the last, and
  963. continuing to the first one.
  964.      One final note worth mentioning about dynamic array storage is the
  965. location in memory of the first array element.  For numeric arrays, the
  966. starting address is always zero, within the specified segment.  (A new
  967. segment can start at any 16-byte address boundary, so at most 15 bytes may
  968. be wasted.)  However, BASIC sometimes positions fixed-length string and
  969. TYPE arrays farther into the segment.  BASIC will not allow an array
  970. element to span a segment boundary under any circumstances.  This could
  971. never happen with numeric data, because each element has a length that is a
  972. power of 2.  That is, 16,384 long integer elements will exactly fit in a
  973. single 64K segment.  But when a fixed-length string or TYPE array is
  974. created, nearly any element length may be specified.
  975.      For example, if you use REDIM Array(1 TO 10) AS STRING * 13000,
  976. 130,000 bytes are needed and element 6 would straddle a segment.  To
  977. prevent that from happening, BASIC's dynamic DIM routine fudges the first
  978. element to instead be placed at address 536.  Thus, the last byte in
  979. element 5 will be at the end of the 64K segment, and the first byte in
  980. element 6 will fall exactly at the start of the second 64K code segment. 
  981. The only limitation is that arrays with odd lengths like this can never
  982. exceed 128K in total size, because the inevitable split would occur at the
  983. start of the third segment.  Arrays whose element lengths are a power of 2,
  984. such as 32 or 4096 bytes, do not have this problem.  (Bear in mind that 1K
  985. is actually 1,024 bytes, so 128K really equals 131,072 bytes).  This is
  986. shown graphically below in Figure 2-5.
  987.  
  988.  
  989. Element 10 is the last that evenly fits ─┐
  990. Segment boundary ────┐                   │
  991.                      ■                   ■
  992. ┌┬───┬───┬───┬───┬───╥───┬───┬───┬───┬───┬╥─ ─ ─
  993. │├───┼───┼───┼───┼──■║■──┼───┼───┼───┼───┤║
  994. └┴───┴───┴───┴───┴───╨───┴───┴───┴───┴───┴╨─ ─ ─
  995. ■■ ■   ■             ■
  996. ││ │   │             └──────■ Address 0
  997. ││ │   └────────────────────■ Element 2
  998. ││ └────────────────────────■ Element 1
  999. │└──────────────────────────■ Address 536
  1000. └───────────────────────────■ Address 0
  1001.  
  1002. Figure 2-5
  1003.  
  1004.  
  1005. FAR DATA VERSUS NEAR DATA
  1006. =========================
  1007.  
  1008. You have already used the terms "near" and "far" to describe BASIC's data,
  1009. and now let's see exactly what they mean.  The 8086 family of
  1010. microprocessors that are used in IBM PC and compatible computers use what
  1011. is called a *segmented architecture*.  This means that while an 8086 can
  1012. access a megabyte of memory, it can do so only in 64K blocks at a time. 
  1013. Before you think this is a terrible way to design a CPU, consider the
  1014. alternative.
  1015.      For example, the 68000 family used in the Apple Macintosh and Atari
  1016. computers use linear addressing, whereby any data anywhere may be accessed
  1017. without restriction.  But the problem is that with millions of possible
  1018. addresses, many bytes are needed to specify those addresses.  Because the
  1019. data segment is implied when dealing with an 80x86, a single integer can
  1020. refer to any address quickly and with very little code.  Therefore,
  1021. assembler instructions for the 68000 that reference memory tend to be long,
  1022. making those programs larger.
  1023.      Since being able to manipulate only one 64K segment is restrictive,
  1024. the 8086's designers provided four different segment registers.  One of
  1025. these, the DS (Data Segment) register, is set to specify a single segment,
  1026. which is then used by the program as much as possible.  This data segment
  1027. is also named DGROUP, and it holds all of the static data in a BASIC
  1028. program.  Again, data in DGROUP can be accessed much faster and with less
  1029. code than can data in any other segment.  In order to assign an element in
  1030. a far array, for example, BASIC requires two additional steps which
  1031. generates additional code.  The first step is to retrieve the array's
  1032. segment from the array descriptor, and the second is to assign the ES
  1033. (Extra Segment) register to access the data.
  1034.      Far data in a BASIC program therefore refers to any data that is
  1035. outside of the 64K DGROUP segment.  Technically, this could encompass the
  1036. entire 1 Megabyte that DOS recognizes, however the memory beyond 640K is
  1037. reserved for video adapters, the BIOS, expanded memory cards, and the like. 
  1038. BASIC uses far memory (outside the 64K data segment but within the first
  1039. 640K) for numeric, fixed-length string, and TYPE arrays, although BASIC PDS
  1040. can optionally store conventional strings there when the /fs (Far String)
  1041. option is used.  Communications buffers are also kept in far memory, and
  1042. this is where incoming characters are placed before your program actually
  1043. reads them.
  1044.      Near memory is therefore very crowded, with many varied types of data
  1045. competing for space.  Earlier I stated that all variables, static arrays,
  1046. and quoted strings are stored in near memory (DGROUP).  But other BASIC
  1047. data is also stored there as well.  This includes DATA items, string
  1048. descriptors, array descriptors, the stack, file buffers, and the internal
  1049. working variables used by BASIC's run-time library routines.
  1050.      When you open a disk file for input, an area in near memory is used as
  1051. a buffer to improve the speed of subsequent reads.  And like subprograms
  1052. and function that you write, BASIC's internal routines also need their own
  1053. variables to operate.  For example, a translation table is maintained in
  1054. DGROUP to relate the file numbers you use when opening a file to the file
  1055. handles that DOS issues.
  1056.      One final note on the items that compete for DGROUP is that in many
  1057. cases data is stored *twice*.  When you use READ to assign a string from a
  1058. DATA item, the data itself remains at the data statement, and is also
  1059. duplicated in the string being assigned.  There is simply no way to remove
  1060. the original data.  Similarly, when you assign a string from a constant as
  1061. in Message$ = "Press any key", the original quoted string is always
  1062. present, and Message$ receives a second copy.  When string space is very
  1063. tight, the only purely BASIC solution is to instead store the data in a
  1064. disk file.
  1065.      Speaking of DATA, bear in mind that reading numeric variables is
  1066. relatively slow and often even more wasteful.  Since all DATA items are
  1067. stored as strings, each time you use READ the VAL routine is called
  1068. internally by BASIC.  VAL is not a particularly fast operation, because of
  1069. the complexity of what it must do.  Worse, by storing numbers as strings,
  1070. even more memory can be wasted than you might think.  For example, storing
  1071. an integer value such as -20556 requires six bytes as a string, even though
  1072. it will be placed ultimately into a two-byte integer.
  1073.  
  1074.  
  1075. ASSESSING MEMORY WITH FRE()
  1076.  
  1077. Since memory is very important to the operation of most programs, it is
  1078. often useful to know how much of it is available at any given moment. 
  1079. BASIC provides the FRE function to do this, however there are a number of
  1080. variations in its use.  Let's take an inside look at the various forms of
  1081. FRE, and see how they can be put to good use.
  1082.      There are no less than six different arguments that can be used with
  1083. FRE.  The first to consider is FRE(0), which reports the amount of free
  1084. string space but without first compacting the string pool.  Therefore, the
  1085. value returned by FRE(0) may be much lower than what actually could be
  1086. available.  FRE when used with a string argument, for example FRE("") or
  1087. FRE(Temp$), also returns the amount of DGROUP memory that is available,
  1088. however it first calls the heap compaction routines.  This guarantees that
  1089. the size reported accurately reflects what is really available.
  1090.      Although FRE(0) may seem to be of little value, it is in fact much
  1091. faster than FRE when a string argument is given.  Therefore, you could
  1092. periodically examine FRE(0), and if it becomes unacceptably low use FRE("")
  1093. to determine the actual amount of memory that is available.  With BASIC PDS
  1094. far strings, FRE(0) is illegal, FRE("") reports the number of bytes
  1095. available for temporary strings, and FRE(Any$) reports the free size of the
  1096. segment in which Any$ resides.  Temporary strings were discussed earlier,
  1097. when we saw how they are used when passing fixed-length string arguments to
  1098. procedures.
  1099.      FRE(-1) was introduced beginning with QuickBASIC 1, and it reports the
  1100. total amount of memory that is currently available for use with far arrays. 
  1101. Thus, you could use it in a program before dimensioning a large numeric
  1102. array, to avoid receiving an "Out of memory" error which would halt your
  1103. program.  Although there is a distinction between near and far memory in
  1104. any PC program, BASIC does an admirable job of making available as much
  1105. memory as you need for various uses.  For example, it is possible to have
  1106. plenty of near memory available, but not enough for all of the dynamic
  1107. arrays that are needed.  In this case, BASIC will reduce the amount of
  1108. memory available in DGROUP, and instead relinquish it for far arrays.
  1109.      FRE(-1) is also useful if you use SHELL within your programs, because
  1110. at least 20K or so of memory is needed to load the necessary additional
  1111. copy of COMMAND.COM.  It is interesting to observe that not having enough
  1112. memory to execute a SHELL results in an "Illegal function call" error,
  1113. rather than the expected "Out of memory".
  1114.      FRE(-2) was added to QuickBASIC beginning with version 4.0, and it
  1115. reports the amount of available stack space.  The stack is a special area
  1116. within DGROUP that is used primarily for passing the addresses of variables
  1117. and other data to subroutines.  The stack is also used to store variables
  1118. when the STATIC option is omitted from a subprogram or function definition. 
  1119. I will discuss static and non-static subroutines later in Chapter 3, but
  1120. for now suffice it to say that enough stack memory is necessary when many
  1121. variables are present and STATIC is omitted.
  1122.      FRE(-3) was added with BASIC PDS, mainly for use within the QBX
  1123. editing environment.  This newest variant reports the amount of expanded
  1124. (EMS) memory that is available, although EMS cannot be accessed by your
  1125. programs directly using BASIC statements.  However, QBX uses that memory to
  1126. store subroutines and optionally numeric, fixed-length, and TYPE arrays. 
  1127. The ISAM file handler that comes with BASIC PDS can also utilize expanded
  1128. memory, as can the PDS overlay manager.
  1129.  
  1130.  
  1131. SETMEM AND STACK
  1132.  
  1133. Besides the various forms of the FRE function, SETMEM can be used to assess
  1134. the size of the far heap, as well as modify that size if necessary.  The
  1135. STACK function is available only with BASIC PDS, and it reports the largest
  1136. possible size the stack can be set to.  Let's see how these functions can
  1137. be useful to you.
  1138.      Although SETMEM is technically a function (because it returns
  1139. information), it is also used to re-size the far heap.  When given an
  1140. argument of zero, SETMEM returns the current size of the far heap.  
  1141. However, this value is not the amount of memory that is free.  Rather, it
  1142. is the maximum heap size regardless of what currently resides there.  The
  1143. following short program shows this in context.
  1144.  
  1145.  
  1146. PRINT SETMEM(0)         'display the heap size
  1147. REDIM Array!(10000)     'allocate 40,000 bytes
  1148. PRINT SETMEM(0)         'the total size remains
  1149.  
  1150. Displayed result (the numbers will vary):
  1151.  
  1152. 276256
  1153. 276256
  1154.  
  1155.  
  1156. When a program starts, the far heap is set as large as possible by BASIC
  1157. and DOS, which is sensible in most cases.  But there are some situations in
  1158. which you might need to reduce that size, most notably when calling C
  1159. routines that need to allocate their own memory.  Also, BASIC moves arrays
  1160. around in the far heap as arrays are dimensioned and then erased.  This is
  1161. much like the near heap string compaction that is performed periodically. 
  1162. If the far heap were not rearranged periodically, it is likely that many
  1163. small portions would be available, but not a single block sufficient for a
  1164. large array.
  1165.      In some cases a program may need to claim memory that is guaranteed
  1166. not to move.  Therefore, you could ask SETMEM to relinquish a portion of
  1167. the far heap, and then call a DOS interrupt to claim that memory for your
  1168. own use.  (DOS provides services to allocate and release memory, which C
  1169. and assembly language programs use to dimension arrays manually.)  Unlike
  1170. BASIC, DOS does not use sophisticated heap management techniques, therefore
  1171. the memory it manages does not move.  I will discuss using SETMEM this way
  1172. later on in Chapter 12.
  1173.      Finally, the STACK function will report the largest amount of memory
  1174. that can be allocated for use as a stack.  Like SETMEM, it doesn't reflect
  1175. how much of that memory is actually in use.  Rather, it simply reports how
  1176. large the stack could be if you wanted or needed to increase it.  Because
  1177. the stack resides in DGROUP, its maximum possible size is dependent on how
  1178. many variables and other data items are present.
  1179.      When run in the QBX environment, the following program fragment shows
  1180. how creating a dynamic string array reduces the amount of memory that could
  1181. be used for the stack.  Since the string descriptors are kept in DGROUP,
  1182. they impinge on the potentially available stack space.
  1183.  
  1184.  
  1185. PRINT STACK
  1186. REDIM Array$(1000)
  1187. PRINT STACK
  1188. ERASE Array$
  1189. PRINT STACK
  1190.  
  1191. Displayed result:
  1192.  
  1193. 47904
  1194. 43808
  1195. 47904
  1196.  
  1197.  
  1198. Since BASIC PDS does not support FRE(0), the STACK function can be used to
  1199. determine how much near memory is available.  The only real difference
  1200. between FRE(0) and STACK is that STACK includes the current stack size,
  1201. where FRE(0) does not.  The STACK function is mentioned here because it
  1202. relates to assessing how much memory is available for data.  Sizing the
  1203. stack will be covered in depth in Chapter 3, when we discuss subprograms,
  1204. functions, and recursion.
  1205.  
  1206.  
  1207. VARPTR, VARSEG, AND SADD
  1208.  
  1209. One of the least understood aspects of BASIC programming is undoubtedly the
  1210. use of VARPTR and its related functions, VARSEG and SADD.  Though you
  1211. probably already know that VARPTR returns the address of a variable, you
  1212. might be wondering how that information could be useful.  After all, the
  1213. whole point of a high-level language such as BASIC is to shield the
  1214. programmer from variable addresses, pointers, and other messy low-level
  1215. details.  And by and large, that is correct.  Although VARPTR is not a
  1216. particularly common function, it can be invaluable in some programming
  1217. situations.
  1218.      VARPTR is a built-in BASIC function which returns the address of any
  1219. variable.  VARSEG is similar, however it reports the memory segment in
  1220. which that address is located.  SADD is meant for use with conventional
  1221. (not fixed-length) strings only, and it tells the address where the first
  1222. character in a string begins.  In BASIC PDS, SSEG is used instead of VARSEG
  1223. for conventional strings, to identify the segment in which the string data
  1224. is kept.  Together, these functions identify the location of any variable
  1225. in memory.
  1226.      The primary use for VARPTR in purely BASIC programming is in
  1227. conjunction with BSAVE and BLOAD, as well as PEEK and POKE.  For example,
  1228. to save an entire array quickly to a disk file with BSAVE, you must specify
  1229. the address where the array is located.  In most cases VARSEG is also
  1230. needed, to identify the array's segment as well.  When used on all simple
  1231. variables, static arrays, and all string arrays, VARSEG returns the normal
  1232. DGROUP segment.  When used on a dynamic numeric array, it instead returns
  1233. the segment at the which the specified element resides.
  1234.      The short example below creates and fills an integer array, and then
  1235. uses VARSEG and VARPTR to save it very quickly to disk.
  1236.  
  1237. REDIM Array%(1 TO 1000)
  1238.  
  1239. FOR X% = 1 TO 1000
  1240.   Array%(X%) = X%
  1241. NEXT
  1242.  
  1243. DEF SEG = VARSEG(Array%(1))
  1244. BSAVE "ARRAY.DAT", VARPTR(Array%(1)), 2000
  1245.  
  1246.  
  1247. Here, DEF SEG indicates in which segment the data that BSAVE will be saving
  1248. is located.  VARPTR is then used to specify the address within that
  1249. segment.  The 2000 tells BSAVE how many bytes are to be written to disk,
  1250. which is determined by multiplying the number of array elements times the
  1251. size of each element.  We will come back to using VARPTR repeatedly in
  1252. Chapter 12 when we discuss accessing DOS and BIOS services with CALL
  1253. Interrupt.  However, it is important to point out here exactly how VARPTR
  1254. and VARSEG work with each type of variable.
  1255.      When VARPTR is used with a numeric variable, as in Address =
  1256. VARPTR(Value!), the address of the first byte in memory that the variable
  1257. occupies is reported.  Value! is a single-precision variable which spans
  1258. four bytes of memory, and it is the lowest of the four addresses that is
  1259. returned.  Likewise, VARPTR when used with static fixed-length string and
  1260. TYPE variables reports the lowest address where the data begins.  But when
  1261. you ask for the VARPTR of a string variable, what is returned is the
  1262. address of the string's descriptor.
  1263.      To obtain the address of the actual data in a string requires the SADD
  1264. (String Address) function.  Internally, BASIC simply looks at the address
  1265. portion of the string descriptor to retrieve the address.  Likewise, the
  1266. LEN function also gets its information directly from the descriptor.  When
  1267. used with any string, VARSEG always reports the normal DGROUP data segment,
  1268. because that is where all strings and their descriptors are kept.
  1269.      Beginning with BASIC PDS and its support for far strings, the SSEG
  1270. function was added to return the segment where the string's data is stored. 
  1271. But even when far strings are being used, VARSEG always returns the segment
  1272. for the descriptor, which is in DGROUP.
  1273.      SADD is not legal with a fixed-length string, and you must instead use
  1274. VARPTR.  Perhaps in a future version BASIC will allow either to be used
  1275. interchangeably.  SADD is likewise illegal for use with the fixed-length
  1276. string portion of a TYPE variable or array.  Again, VARPTR will return the
  1277. address of any component in a TYPE, within the segment reported by VARSEG.
  1278.      Another important use for VARPTR is to assist passing arrays to
  1279. assembly language routines.  When a single array element is specified using
  1280. early versions of Microsoft compiled BASIC, the starting address of the
  1281. element is sent as expected.  Beginning with QuickBASIC 4.0 and its support
  1282. for far data residing in multiple segments, a more complicated arrangement
  1283. was devised.  Here's how that works.
  1284.      When an element in a dynamic array is passed as a parameter, BASIC
  1285. makes a copy of the element into a temporary variable in near memory, and
  1286. then sends the address of the copy.  When the routine returns, the data in
  1287. the temporary variable is copied back to the original array element, in
  1288. case the called routine changed the data.  In many cases this behavior is
  1289. quite sensible, since the called routine can assume that the variable is in
  1290. near memory and thus operate that much faster.
  1291.      Further, BASIC subroutines *require* a non-array parameter (not passed
  1292. with empty parentheses) to be in DGROUP.  That is, any time a single
  1293. element in an integer array is passed to a routine, that routine would be
  1294. designed to expect a single integer variable.  This is shown in the brief
  1295. example below, where a single element in an array is passed, as opposed to
  1296. the entire array.
  1297.  
  1298.  
  1299. REDIM Array%(1 TO 100)
  1300. Array%(25) = -14
  1301. CALL MyProc(Array%(25))      'pass one element
  1302. .
  1303. .
  1304. .
  1305. SUB MyProc(IntVar%) STATIC   'this sub expects a
  1306.   PRINT IntVar%              '  single variable
  1307. END SUB
  1308.  
  1309. Displayed result:
  1310.  
  1311. -14
  1312.  
  1313.  
  1314. Unfortunately, this copying not only generates a lot of extra code to
  1315. implement, it also takes memory from DGROUP to hold the copy, and that
  1316. memory is taken permanently.  Worse still, *each* occurrence of an array
  1317. element passed in a CALL statement reserves however many bytes are needed
  1318. to store the element.  For a large TYPE structure this can be a lot of
  1319. memory indeed!
  1320.      So you won't think that I'm being an alarmist about this issue, here
  1321. are some facts based on programs compiled using BASIC 7.1 PDS.  These
  1322. examples document the amount of additional code that is generated to pass a
  1323. near string array element as an argument to a subprogram or function.
  1324.      Passing a string array element requires 56 bytes when a copy is made,
  1325. compared to only 17 when it is not.  The same operations in QuickBASIC 4.5
  1326. create 47 and 18 bytes respectively, so QB 4.5 is actually better when
  1327. making the copy, but a tad worse when not.  The code used in these examples
  1328. is shown below, and Array$ is a dynamic near string array.  (I will explain
  1329. the purpose of BYVAL in just a moment.)  Again, the difference in byte
  1330. counts reflects the additional code that BC creates to assign and then
  1331. delete the temporary copies.
  1332.  
  1333.  
  1334. CALL Routine(Array$(2))
  1335. CALL Routine(BYVAL VARPTR(Array$(2)))
  1336.  
  1337.  
  1338. Worse still, with either compiler 73 bytes of code are created to pass an
  1339. element in a TYPE array the usual way, compared to 18 when the copying is
  1340. avoided.  And this byte count does not include the DGROUP memory required
  1341. to hold the copy.  Is that reduction in code size worth working for?  You
  1342. bet it is!  And best of all, hardly any extra effort is needed to avoid
  1343. having BASIC make these copies--just the appropriate knowledge.
  1344.      The key, as you can see, is VARPTR.  If you are calling an assembly
  1345. language routine that expects a string and you want to pass an element from
  1346. a string array, you must use BYVAL along with VARPTR.  CALL Routine(BYVAL
  1347. VARPTR(Array$(Element))) is functionally identical to CALL
  1348. Routine(Array$(Element)), although they sure do look different!  In either
  1349. case, the integer address of a string is passed to the routine.
  1350.      Unlike the usual way that BASIC passes a variable by sending its
  1351. address, BYVAL instead sends the actual data.  In this case, the value of
  1352. an address is what we wanted to begin with anyway.  (Without the BYVAL,
  1353. BASIC would make a temporary copy of the integer value that VARPTR returns,
  1354. and send the address of that copy.)  Best of all, asking for the address
  1355. directly defeats the built-in copying mechanism.  Although creating a copy
  1356. of a far numeric array element is sensible as we saw earlier, it is not
  1357. clear to me why BC does this with string array data that is in DGROUP
  1358. already.
  1359.      Although you can't normally send an integer--which is what VARPTR
  1360. actually returns--to a BASIC subprogram that expects a string, you can if
  1361. that subprogram is in a different file and the files are compiled
  1362. separately.  This will also work if the BASIC code has been pre-compiled
  1363. and placed in a Quick Library.
  1364.      But there is another, equally important reason to use VARPTR with
  1365. array elements.  If you are calling an assembler routine that will sort an
  1366. array, it must have access to the array element's address, and not the
  1367. address of a copy.  All of the elements in any array are contiguous, and a
  1368. sort routine would need to know where in memory the first element is
  1369. located.  From that it can then access all of the successive elements. 
  1370. With VARPTR we are telling BASIC that what is needed is the actual address
  1371. of the specified element.
  1372.      Bear in mind that this relates primarily to passing arrays to assembly
  1373. language (and possibly C) routines only.  After all, if you are designing a
  1374. sort routine using purely BASIC commands, you would pass and receive the
  1375. array using empty parentheses.  Indeed, this is yet another important
  1376. advantage that BASIC holds over C and Pascal, since neither of those
  1377. languages have array descriptors.  Writing a sort routine in C requires
  1378. that *you* do all of the work to locate and compare each element in turn,
  1379. based on some base starting address.
  1380.      There is one final issue that we must discuss, and that is passing far
  1381. array data to external assembly language routines.  I already explained
  1382. that by making a copy of a far array element, the called routine does not
  1383. have to be written to deal with far (two-word segmented) addresses.  But in
  1384. some cases, writing a routine that way will be more efficient.  Further,
  1385. like C, assembly language routines thrive on manipulating pointers to data. 
  1386. Although an assembler routine could be written to read the segment and
  1387. address from the array descriptor, this is not a common method.  One reason
  1388. is that if Microsoft changes the format of the descriptor, the routine will
  1389. no longer work.  Another is that it is frankly easier to have the caller
  1390. simply pass the full segmented address of the first element.
  1391.      This brings us to the SEG directive, which is a combination of BYVAL
  1392. and VARPTR and also BYVAL and VARSEG. As with BYVAL VARPTR, using SEG
  1393. before a variable or array element in a call tells BASIC that the value of
  1394. the array's full address is needed.  A typical example would be CALL
  1395. Routine(SEG Array#(1)), and in this case, BASIC sends not one address word
  1396. but two to the routine.
  1397.      You could also pass the full address of an array element by value
  1398. using VARSEG and VARPTR, and this next example produces the identical
  1399. result: CALL Routine(BYVAL VARSEG(Array#(1)), BYVAL VARPTR(Array#(1))). 
  1400. Using SEG results in somewhat less code, though, because BASIC will obtain
  1401. the segment and address in a single operation.  In fact, this is one area
  1402. where the compiler does a poor job of optimizing, because using VARSEG and
  1403. VARPTR in a single program statement generates a similar sequence of code
  1404. twice.
  1405.      There is one unfortunate complication here, which arises when SEG is
  1406. used with a fixed-length string array.  What SEG *should* do in that case
  1407. is pass the segmented address of the specified element.  But it doesn't. 
  1408. Instead, BASIC creates a temporary copy of the specified element in a
  1409. conventional dynamic string, and then passes the segmented address of the
  1410. copy's descriptor.  Of course, this is useless in most programming
  1411. situations.
  1412.      There are two possible solutions to this problem.  The first is to use
  1413. the slightly less efficient BYVAL VARSEG and BYVAL VARPTR combination as
  1414. shown above.  The second solution is to create an equivalent fixed-length
  1415. string array by using a dummy TYPE that is comprised solely of a single
  1416. string component.  Since TYPE variables are passed correctly when SEG is
  1417. used, using a TYPE eliminates the problem.  Both of these methods are shown
  1418. in the listing that follows.
  1419.  
  1420.  
  1421. '----- this creates more code and looks clumsy
  1422.  
  1423. REDIM Array(1 TO 1000) AS STRING * 50
  1424. CALL Routine(BYVAL VARSEG(Array(1)), BYVAL VARPTR(Array(1)))
  1425.  
  1426.  
  1427. '----- this creates less code and reads clearly
  1428.  
  1429. TYPE FLen
  1430.   S AS STRING * 100
  1431. END TYPE
  1432. REDIM Array(1 TO 1000) AS FLen
  1433. CALL Routine(SEG Array(1))
  1434.  
  1435.  
  1436. Although SEG looks like a single parameter is being passed, in fact two
  1437. integers are sent to the called routine--a segment and an address.  This is
  1438. why a single SEG can replace both a VARSEG and a VARPTR in one call. 
  1439. Chapter 13 will return to BYVAL, VARPTR, and SEG, though the purpose there
  1440. will be to learn how to write routines that accept such parameters.
  1441.  
  1442.  
  1443. CONSTANTS
  1444. =========
  1445.  
  1446. The final data type to examine is constants.  By definition, a constant is
  1447. simply any value that does not change, as opposed to a variable that can. 
  1448. For example, in the statement I% = 10, the value 10 is a constant. 
  1449. Similarly, the quoted string "Hello" is a constant when you write PRINT
  1450. "Hello".
  1451.      There are two types of constants that can appear in a BASIC program. 
  1452. One is simple numbers and quoted strings as described above, and the other
  1453. is the named constant which is defined using a CONST statement.  For
  1454. example, you can write CONST MaxRows = 25 as well as CONST Message$ =
  1455. "Insert disk in drive", and so forth.  It is even possible to define one
  1456. CONST value based on a previous one, as in CONST NumRows = 25, ScrnSize =
  1457. NumRows * 80.  Then, you could use these meaningful names later in the
  1458. program, instead of the values they represent.
  1459.      It is important to understand that using named constants is identical
  1460. to using the numbers themselves.  The value of this will become apparent
  1461. when you see the relative advantages and disadvantages of using numbers as
  1462. opposed to variables.  Let's begin this discussion of numbers with how they
  1463. are stored by the compiler.  Or rather, how they are sometimes stored.
  1464.      When a CONST statement is used in a BASIC program, BASIC does
  1465. absolutely nothing with the value, other than to remember that you defined
  1466. it.  Therefore, you could have a hundred CONST statements which are never
  1467. used, and the final .EXE program will be no larger than if none had been
  1468. defined.  If a CONST value is used as an argument to, say, LOCATE or
  1469. perhaps as a parameter to a subroutine, BASIC simply substitutes the value
  1470. you originally gave it.  When a variable is assigned as in Value% = 100,
  1471. BASIC sets aside memory to hold the variable.  With a constant definition
  1472. such as CONST Value% = 100, no memory is set aside and BASIC merely
  1473. remembers that any use of Value% is to be replaced by the number 100.  But
  1474. how are these numbers represented internally.
  1475.      When you create an integer assignment such as Count% = 5, the BASIC
  1476. compiler generates code to move the value 5 into the integer variable, as
  1477. you saw in Chapter 1.  Therefore, the actual value 5 is never stored as
  1478. data anywhere.  Rather, it is placed into the code as part of an assembly
  1479. language instruction.
  1480.      Now, if you instead assign a single or double precision variable from
  1481. a number--and again it doesn't matter whether that number is a literal or a
  1482. CONST--the appropriate floating point representation of that number is
  1483. placed in DGROUP at compile time, and then used as the source for a normal
  1484. floating point assignment.  That is, it is assigned as if it were a
  1485. variable.
  1486.      There is no reasonable way to imbed a floating point value into an
  1487. assembly language instruction, because the CPU cannot deal with such values
  1488. directly.   Therefore, assigning X% = 3 treats the number 3 as an integer
  1489. value, while assigning Y# = 3 treats it as a double precision value. 
  1490. Again, it doesn't matter whether the 3 is a literal number as shown here,
  1491. or a CONST that has been defined.  In fact, if you use CONST Three! = 3, a
  1492. subsequent assignment such as Value% = Three! treats Three! as an integer
  1493. resulting in less resultant code.  As you can see, the compiler is
  1494. extremely smart in how it handles these constants, and it understands the
  1495. context in which they are being used.
  1496.      In general, BASIC uses the minimum precision possible when
  1497. representing a number.  However, you can coerce a number to a different
  1498. precision with an explicit type identifier.  For example, if you are
  1499. calling a routine in a separate module that expects a double precision
  1500. value, you could add a pound sign (#) to the number like this: CALL
  1501. Something(45#).  Without the double precision identifier, BASIC would treat
  1502. the 45 as an integer, which is of course incorrect.
  1503.      Likewise, BASIC can be forced to evaluate a numeric expression that
  1504. might otherwise overflow by placing a type identifier after it.  One
  1505. typical situation is when constructing a value from two byte portions.  The
  1506. usual way to do this would be Value& = LoByte% + 256 * HiByte%.  Although
  1507. the result of this expression can clearly fit into the long integer no
  1508. matter what the values of LoByte% and HiByte% might be, an overflow error
  1509. can still occur.  (But as we saw earlier, this will happen only in the QB
  1510. environment, or if you have compiled to disk with the /d debugging option.)
  1511.      The problem arises when HiByte% is greater than 127, because the
  1512. result of multiplying HiByte% times 256 exceeds the capacity of a regular
  1513. integer.  Normally, BASIC is to be commended for the way it minimizes
  1514. overhead by reducing calculations to the smallest possible data type.  But
  1515. in this case it creates a problem, because the result cannot be expressed
  1516. as an integer.
  1517.      The solution, then, is to add an ampersand after the 256, as in Value&
  1518. = LoByte% + 256& * HiByte%.  By establishing the value 256 as a long
  1519. integer, you are telling BASIC to perform the calculation to the full
  1520. precision of a long integer.  And since the result of the multiplication is
  1521. treated as a long integer, so is the addition of that result to LoByte%.  A
  1522. single precision exclamation point could also be used, but that would
  1523. require a floating point multiplication.  Since a long integer multiply is
  1524. much faster and needs less code, this is the preferred solution.
  1525.      One final item worth noting is the way the QB and QBX editing
  1526. environments sometimes modify constants.  For example, if you attempt to
  1527. enter a statement such as Value! = 1.0, you will see the constant changed
  1528. to read 1! instead.  This happens when you press Enter to terminate the
  1529. line.  Similarly, if you write D# = 1234567.8901234, BASIC will add a
  1530. trailing pound sign to the number.  This behavior is your clue that these
  1531. numbers are being stored internally as single and double precision values
  1532. respectively.
  1533.  
  1534.  
  1535. PASSING NUMERIC CONSTANTS TO A PROCEDURE
  1536.  
  1537. Normally, any constant that could be an integer is passed to a subprogram
  1538. or function as an integer.  That is, calling an external procedure as in
  1539. CALL External(100) passes the 100 as an integer value.  If the called
  1540. routine has been designed to expect a variable of a different type, you
  1541. must add the appropriate type identifier.  If a long integer is expected,
  1542. for example, you must use CALL External(100&).  If, on the other hand, the
  1543. called routine is in the same module (that is, the same physical source
  1544. file), QB will create a suitable DECLARE statement automatically.  This
  1545. lets QB and BC know what is expected so they can pass the value in the
  1546. correct format.  Thus, BASIC is doing you a favor by interpreting the
  1547. constant's type in a manner that is relevant to your program.
  1548.      This "favor" has a nasty quirk, though.  If you are developing a
  1549. multi-module program in the QuickBASIC editor, the automatic type
  1550. conversion is done for you automatically, even when the call is to a
  1551. different module.  Your program uses, say, CALL Routine(25), and QB or QBX
  1552. send the value in the correct format automatically.  But when the modules
  1553. are compiled and linked, the same program that had worked correctly in the
  1554. environment will now fail.
  1555.      Since each module in a multi-module program is compiled separately, BC
  1556. has no way to know what the called routine actually expects.  In fact, this
  1557. is one of the primary purposes of the DECLARE statement--to advise BASIC as
  1558. to how arguments are to be passed.  For example, DECLARE SUB
  1559. Marine(Trident!) tells BASIC that any constant passed to Marine is to be
  1560. sent as a single precision value.  You could optionally use the AS SINGLE
  1561. directive, thus: DECLARE SUB Marine(Trident AS SINGLE).  In general, I
  1562. prefer the more compact form since it conveys the necessary information
  1563. with less clutter.
  1564.      Another important use for adding a type identifier to a numeric
  1565. constant is to improve a program's accuracy.  Running the short program
  1566. below will illustrate this in context.  Although neither answer is entirely
  1567. accurate, the calculation that uses the double precision constant is much
  1568. closer.  In this case, a decimal number that does not have an explicit type
  1569. identifier is assumed to have only single precision accuracy.  That is, the
  1570. value is stored in only four bytes instead of eight.
  1571.  
  1572.  
  1573. FOR X% = 1 TO 10000
  1574.   Y# = Y# + 1.1
  1575.   Z# = Z# + 1.1#
  1576. NEXT
  1577. PRINT Y#, Z#
  1578.  
  1579. Displayed result:
  1580. 11000.00023841858     11000.00000000204
  1581.  
  1582.  
  1583. You have already learned that BASIC often makes a temporary copy of a
  1584. variable when calling a subprogram or function.  But you should know that
  1585. this also happens whenever a constant is passed as an argument.  For
  1586. example, in a function call such as Result = Calculate!(Value!, 100), where
  1587. Calculate! has been declared as a function, the integer value 100 is copied
  1588. to a temporary location.  Since BASIC procedures require the address of a
  1589. parameter, a temporary variable must be created and the address of that
  1590. variable passed.  The important point to remember is that for each
  1591. occurrence of a constant in a CALL or function invocation, a new area of
  1592. DGROUP is taken.
  1593.      You might think that BASIC should simply store a 100 somewhere in
  1594. DGROUP once, and then pass the address of that value.  Indeed, this would
  1595. save an awful lot of memory when many constants are being used.  The reason
  1596. this isn't done, however, is that subroutines can change incoming
  1597. parameters.  Therefore, if a single integer 100 was stored and its address
  1598. passed to a routine that changed it, subsequent calls using 100 would
  1599. receive an incorrect value.
  1600.      The ideal solution to this problem is to create a variable with the
  1601. required value.  For example, if you are now passing the value 2 as a
  1602. literal many times in a program, instead assign a variable, perhaps named
  1603. Two%, early in your program.  That is, Two% = 2.  Then, each time you need
  1604. that value, instead pass the variable.  For the record, six bytes are
  1605. needed to assign an integer such as Two%, and four bytes are generated each
  1606. time that variable is passed in a call.
  1607.      Contrast that to the 10 bytes generated to create and store a
  1608. temporary copy and pass its address, not including the two bytes the copy
  1609. permanently takes from near memory.  Even if you use the value only twice,
  1610. the savings will be worthwhile (24 vs. 30 bytes).  Because a value of zero
  1611. is very common, it is also an ideal candidate for being replaced with a
  1612. variable.  Even better, you don't even have to assign it!  That is, CALL
  1613. SomeProc(Zero%) will send a zero, without requiring a previous Zero% = 0
  1614. assignment.
  1615.  
  1616.  
  1617. STRING CONSTANTS
  1618. ================
  1619.  
  1620. Like numeric constants, string constants that are defined in a CONST
  1621. statement but never referenced will not be added to the final .EXE file. 
  1622. Constants that are used--whether as literals or as CONST statements--are
  1623. always stored in DGROUP.  If your program has the statement PRINT "I like
  1624. BASIC", then the twelve characters in the string are placed into DGROUP. 
  1625. But since the PRINT statement requires a string descriptor in order to
  1626. locate the string and determine its length, an additional four bytes are
  1627. allocated by BASIC just for that purpose.  Variables are always stored at
  1628. an even-numbered address, so odd-length strings also waste one extra byte.
  1629.      Because string constants have a ferocious appetite for near memory, BC
  1630. has been designed to be particularly intelligent in the way they are
  1631. handled.  Although there is no way to avoid the storage of a descriptor for
  1632. each constant, there is another, even better trick that can be employed. 
  1633. For each string constant you reference in a program that is longer than
  1634. four characters, BC stores it only once.  Even if you have the statement
  1635. PRINT "Press any key to continue" twenty-five times in your program, BC
  1636. will store the characters just once, and each PRINT statement will refer to
  1637. the same string.
  1638.      In order to do this, the compiler must remember each string constant
  1639. it encounters as it processes your program, and save it in an internal
  1640. working array.  When many string constants are being used, this can cause
  1641. the compiler to run out of memory.  Remember, BC has an enormous amount of
  1642. information it must deal with as it processes your BASIC source file, and
  1643. keeping track of string constants is but one part of the job.
  1644.      To solve this problem Microsoft has provided the /s (String) option,
  1645. which tells BC not to combine like data.  Although this may have the net
  1646. effect of making the final .EXE file larger and also taking more string
  1647. space, it may be the only solution with some large programs.  Contrary to
  1648. the BASIC documentation, however, using /s in reality often makes a program
  1649. *smaller*.  This issue will be described in detail in Chapter 5, where all
  1650. of the various BC command line options are discussed.
  1651.  
  1652.  
  1653.  
  1654. PASSING STRING CONSTANTS TO A PROCEDURE
  1655.  
  1656. As you have repeatedly seen, BASIC often generates additional code to
  1657. create copies of variables and constants.  It should come as no surprise,
  1658. therefore, to learn that this happens with string constants as well.  When
  1659. you print the same string more than once in a program, BASIC knows that its
  1660. own PRINT routine will never change the data.  But as with numeric
  1661. constants, if you send a string constant to a subprogram or function, there
  1662. is no such guarantee.
  1663.      For example, if you have a statement such as CALL PrintIt(Work$) in
  1664. your program, it is very possible--even likely--that the PrintIt routine
  1665. may change or reassign its incoming parameter.  Even if *you* know that
  1666. PrintIt will not change the string, BASIC has no way to know this.  To
  1667. avoid any possibility of that happening, BASIC generates code to create a
  1668. temporary copy of every string constant that is used as an argument.  And
  1669. this is done for every call.  If the statement CALL PrintMessage("Press a
  1670. key") appears in your program ten times, then code to copy that message is
  1671. generated ten times!
  1672.      Beginning with BASIC 7.1 PDS, you can now specify that variables are
  1673. to be sent by value to BASIC procedures.  This lets you avoid the creation
  1674. of temporary copies, and this subject will also be explored in more detail
  1675. in Chapter 3.
  1676.      With either QuickBASIC 4.5 or BASIC PDS, calling a routine with a
  1677. single quoted string as an argument generates 31 bytes of code.  Passing a
  1678. string variable instead requires only nine bytes.  Both of these byte
  1679. counts includes the five bytes to process the call itself.  The real
  1680. difference is therefore 4 bytes vs. 26--for a net ratio of 6.5 to 1.  (Part
  1681. of those 31 bytes is code that erases the temporary string.)  So as with
  1682. numeric constants that are used more than once, your programs will be
  1683. smaller if a variable is assigned once, and that variable is passed as an
  1684. argument.
  1685.      While we are on the topic of temporary variables, there is yet another
  1686. situation that causes BASIC to create them.  When the result of an
  1687. expression is passed as an argument, BASIC must evaluate that expression,
  1688. and store the result somewhere.  Again, since nearly all procedures require
  1689. the address of a parameter rather than its value, an address of that result
  1690. is needed.  And without storing the result, there can of course be no
  1691. address.
  1692.      When you use a statement such as CALL Home(Elli + Lou), BASIC
  1693. calculates the sum of Elli plus Lou, and stores that in a reserved place in
  1694. DGROUP which is not used for any other purpose.  That address is then sent
  1695. to the Home routine as if it were a single variable, and Home is none the
  1696. wiser.  Likewise, a string concatenation creates a temporary string, for
  1697. the same reason.  Although the requisite descriptor permanently steals four
  1698. bytes of DGROUP memory, the temporary string itself is erased by BASIC
  1699. automatically after the call.  Thus, the first example in the listing below
  1700. is similar in efficiency to the second.  The four-byte difference is due to
  1701. BASIC calling a special routine that deletes the temporary copy it created,
  1702. as opposed to the slightly more involved code that assigns Temp$ from the
  1703. null string ("") to erase it.
  1704.  
  1705.  
  1706. CALL DoIt(First$ + Last$)  'this makes 41 bytes
  1707.  
  1708. Temp$ = First$ + Last$     'this makes 45 bytes
  1709. CALL DoIt(Temp$)
  1710. Temp$ = ""
  1711.  
  1712.  
  1713. UNUSUAL STRING CONSTANTS
  1714.  
  1715. One final topic worth mentioning is that QuickBASIC also lets you imbed
  1716. control and extended characters into a string constant.  Consider the
  1717. program shown below.  Here, several of the IBM extended characters are used
  1718. to define a box, but without requiring CHR$ to be used repeatedly. 
  1719. Characters with ASCII values greater than 127 can be entered easily by
  1720. simply pressing and holding the Alt key, typing the desired ASCII value on
  1721. the PC's numeric key-pad, and then releasing the Alt key.  This will not
  1722. work using the number keys along the top row of the keyboard.
  1723.  
  1724.  
  1725. DIM Box$(1 TO 4)          'define a box
  1726.  
  1727. Box$(1) = "╔══════════════════╗"
  1728. Box$(2) = "║                  ║"
  1729. Box$(3) = "║                  ║"
  1730. Box$(4) = "╚══════════════════╝"
  1731.  
  1732. FOR X = 1 TO 4            'now display the box
  1733.   PRINT Box$(X)
  1734. NEXT
  1735.  
  1736.  
  1737. To enter control characters (those with ASCII values less than 32) requires
  1738. a different trick.  Although the Alt-keypad method is in fact built into
  1739. the BIOS of all PCs, this next one is specific to QuickBASIC, QBX, and some
  1740. word processor programs.  To do this, first press Ctrl-P, observing the ^P
  1741. symbol that QB displays at the bottom right of the screen.  This lets you
  1742. know that the next control character you press will be accepted literally. 
  1743. For example, Ctrl-P followed by Ctrl-L will display the female symbol, and
  1744. Ctrl-P followed by Ctrl-[ will enter the Escape character.
  1745.      Bear in mind that some control codes will cause unusual behavior if
  1746. your program is listed on a printer.  For example, an embedded CHR$(7) will
  1747. sound the buzzer if your printer has one, a CHR$(8) will back up the print
  1748. head one column, and a CHR$(12) will issue a form feed and skip to the next
  1749. page.  Indeed, you can use this to advantage to intentionally force a form
  1750. feed, perhaps with a statement such as REM followed by the Ctrl-L female
  1751. symbol.
  1752.      I should mention that different versions of the QB editor respond
  1753. differently to the Ctrl-P command.  QuickBASIC 4.0 requires Ctrl-[ to enter
  1754. the Escape code, while QBX takes either Ctrl-[ or the Escape key itself.  I
  1755. should also mention that you must never imbed a CHR$(26) into a BASIC
  1756. source file.  That character is recognized by DOS to indicate the end of a
  1757. file, and BC will stop dead at that point when compiling your program.  QB,
  1758. however, will load the file correctly.
  1759.  
  1760.  
  1761. WOULDN'T IT BE NICE IF DEPT.
  1762. ============================
  1763.  
  1764. No discussion of constants would be complete without a mention of
  1765. initialized data.  Unfortunately, as of this writing BASIC does not support
  1766. that feature!  The concept is simple, and it would be trivial for BASIC's
  1767. designers to implement.  Here's how initialized data works.
  1768.      Whenever a variable requires a certain value, the only way to give it
  1769. that value is to assign it.  Some languages let you declare a variable's
  1770. initial value in the source code, saving the few bytes it takes to assign
  1771. it later.  Since space for every variable is in the .EXE file anyway, there
  1772. would be no additional penalty imposed by adding this capability.  I
  1773. envision a syntax such as DIM X = 3.9 AS SINGLE, or perhaps simply DIM Y% =
  1774. 3, or even DIM PassWord$ = "GuessThis".  Where Y% = 3 creates a six-byte
  1775. code sequence to put the value 3 into Y%, what I am proposing would have
  1776. the compiler place that value there at the time it creates the program.
  1777.      Equally desireable would be allowing string constants to be defined
  1778. using CHR$ arguments.  For example, CONST EOF$ = CHR$(26) would be a
  1779. terrific enhancement to the language, and allowing code such as CONST CRLF$
  1780. = CHR$(13) + CHR$(10) would be even more powerful.  Again, we can only hope
  1781. that this feature will be added in a future version.
  1782.      Yet another constant optimization that BASIC could do but doesn't is
  1783. constant string function evaluation.  In many programming situations the
  1784. programmer is faced with deciding between program efficiency and
  1785. readability.  A perfect example of this is testing an integer value to see
  1786. whether it represents a legal character.  For instance, IF Char < 65 is not
  1787. nearly as meaningful as IF Char < ASC("A").
  1788.      Clearly, BC could and should resolve the expression ASC("A") while it
  1789. is compiling your program, and generate simple code that compares two
  1790. integers.  Instead, it stores the "A" as a one-byte string (which with its
  1791. descriptor takes five bytes), and generates code to call the internal ASC
  1792. function before performing the comparison.  The point here is that no
  1793. matter how intelligent BC is, folks like us will always find some reason to
  1794. complain!
  1795.  
  1796.  
  1797. BIT OPERATIONS
  1798. ==============
  1799.  
  1800. The last important subject this chapter will cover is bit manipulation
  1801. using AND, OR, XOR, and NOT.  These logical operators have two similar, but
  1802. very different, uses in a BASIC program.  The first use--the one I will
  1803. discuss here--is to manipulate the individual bits in an integer or long
  1804. integer variable.  The second use is for directing a program's flow, and
  1805. that will be covered in Chapter 3.
  1806.      Each of the bit manipulation operators performs a very simple Binary
  1807. function.  Most of these functions operate on the contents of two integers,
  1808. using those bits that are in an equivalent position.  The examples shown in
  1809. Figure 2-6 use a single byte only, solely for clarity.  In practice, the
  1810. same operations would be extended to either the sixteen bits in an integer,
  1811. or the 32 bits in a long integer.
  1812.  
  1813.  
  1814. 13 = 0000 1101
  1815. 25 = 0001 1001
  1816.      ─────────
  1817.      0000 1001  result when AND is used
  1818.           ^  ^
  1819.           └──┴──────── both of the bits are set
  1820.                        in each column
  1821.  
  1822.  
  1823. 13 = 0000 1101
  1824. 25 = 0001 1001
  1825.      ─────────
  1826.      0001 1101  result when OR is used
  1827.         ^ ^^ ^
  1828.         └─┴┴─┴──────── one or both bits are set
  1829.                        in each column
  1830.  
  1831.  
  1832. 13 = 0000 1101
  1833. 25 = 0001 1001
  1834.      ─────────
  1835.      0001 0100  result when XOR is used
  1836.         ^  ^
  1837.         └──┴────────── the bits are different
  1838.                        in each column
  1839.  
  1840.  
  1841. 13 = 0000 0000 0000 1101
  1842.      ───────────────────
  1843.      1111 1111 1111 0010  result after using NOT
  1844.  
  1845. Figure 2-6
  1846.  
  1847.  
  1848. The examples given here use the same decimal values 13 and 25, and these
  1849. are also shown in their Binary equivalents.  What is important when viewing
  1850. Binary numbers is to consider the two bits in each vertical column.  In the
  1851. first example, the result in a given column is 1 (or True) only when that
  1852. bit is set in the first number AND the same bit is also set in the second. 
  1853. This condition is true for only two of the bits in these particular
  1854. numbers.  The result bits therefore represent the answer in Binary, which
  1855. in this case is 13 AND 25 = 9.  What is important here is not that 13 AND
  1856. 25 equals 9, but how the bits interact with each other.
  1857.      The second example shows OR at work, and it sets the result bits for
  1858. any position where a given bit is set in one byte OR that bit is set in the
  1859. other.  Of course, if both are set the OR result is also true.  In this
  1860. case, four of the columns have one bit or the other (or both) set to 1.  By
  1861. the way, these results can be proven easily in BASIC by simply typing the
  1862. expression.  That is, PRINT 13 OR 25 will display the answer 29.
  1863.      The third example is for XOR, which stands for Exclusive Or.  XOR sets
  1864. a result bit only when the two bits being compared are different.  Here,
  1865. two of the bits are different, thus 13 XOR 25 = 20.  Again, it is not the
  1866. decimal result we are after, but how the bits in one variable can be used
  1867. to set or clear the bits in another.
  1868.      The NOT operator uses only one value, and it simply reverses all of
  1869. the bits.  Any bit that was a 1 is changed to 0, and any bit that had been
  1870. 0 is now 1.  A full word is used in this example, to illustrate the fact
  1871. that NOT on any positive number makes it negative, and vice versa.  As you
  1872. learned earlier in this chapter, the highest, or left-most bit is used to
  1873. store the sign of a number.  Therefore, toggling this bit also switches the
  1874. number between positive and negative.  In this case, NOT 13 = -14.
  1875.      All of the logical operators can be very useful in some situations,
  1876. although admittedly those situations are generally when accessing DOS or
  1877. interfacing with assembly language routines.  For example, many DOS
  1878. services indicate a failure such as "File not found" by setting the Carry
  1879. flag.  You would thus use AND after a CALL Interrupt to test that bit. 
  1880. Another good application for bit manipulation is to store True or False
  1881. information in each of the sixteen bits in an integer, thus preserving
  1882. memory.  That is, instead of sixteen separate Yes/No variables, you could
  1883. use just one integer.
  1884.      Bit operations can also be used to replace calculations in certain
  1885. situations.  One common practice is to use division and MOD to break an
  1886. integer word into its component byte portions.  The usual way to obtain the
  1887. lower byte is LoByte% = Word% MOD 256, where MOD provides the remainder
  1888. after dividing.  While there is nothing wrong with doing it that way, Word%
  1889. = LoByte% AND 255 operates slightly faster.  Division is simply a slower
  1890. operation than AND, especially on the 8088.  Newer chips such as the 80286
  1891. and 80386 have improved algorithms, and division is not nearly as slow as
  1892. with the older CPU.  Chapter 3 will look at some other purely BASIC uses of
  1893. AND and OR.
  1894.  
  1895.  
  1896. SUMMARY
  1897. =======
  1898.  
  1899. As you have seen in this chapter, there is much more to variables and data
  1900. than the BASIC manuals indicate.  You have learned how data is constructed
  1901. and stored, how the compiler manipulates that data, and how to determine
  1902. for yourself the amount of memory that is needed and is available.  In
  1903. particular, you have seen how data is copied frequently but with no
  1904. indication that this is happening.  Because such copying requires
  1905. additional memory, it is a frequent cause of "Out of memory" errors that on
  1906. the surface appear to be unfounded.
  1907.      You have also learned about BASIC's near and far heaps, and how they
  1908. are managed using string and array descriptors.  With its dynamic
  1909. allocation methods and periodic rearrangement of the data in your program,
  1910. BASIC is able to prevent memory from becoming fragmented.  Although such
  1911. sophisticated memory management techniques require additional code to
  1912. implement, they provide an important service that programmers would
  1913. otherwise have to devise for themselves.
  1914.      Finally, you have learned how the various bit manipulation operations
  1915. in BASIC work.  This chapter will prove to be an important foundation for
  1916. the information presented in upcoming chapters.  Indeed, a thorough
  1917. understanding of data and memory issues will be invaluable when you learn
  1918. about accessing DOS and BIOS services in Chapter 12.
  1919.