home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / progbas / pbwiz12.arj / PBWIZ.DOC < prev    next >
Encoding:
Text File  |  1992-02-28  |  61.6 KB  |  1,643 lines

  1.                 The PowerBASIC Wizard's Library
  2.                 =-----------------------------=
  3.                           Version 1.2
  4.  
  5.      PBWIZ  Copyright (c) 1991-1992  Thomas G. Hanlin III
  6.  
  7.  
  8.  
  9. This is PBWiz, a library of assembly language and BASIC
  10. routines for use with PowerBASIC version 2.x.  Full source code
  11. is available with registration. The PBWiz collection is
  12. copyrighted and may be distributed only under the following
  13. conditions:
  14.  
  15. 1) All PBWiz files must be distributed together in original,
  16.    unaltered form.  A list of the 16 files included may be
  17.    found in FILES.LST.
  18.  
  19. 2) No files may be added to the PBWiz collection.  This applies
  20.    specifically to the practice of adding files containing BBS
  21.    ads to archives.  I find this seriously offensive.
  22.  
  23. You use this library at your own risk.  It has been tested by
  24. me on my own computer, but I will not assume any responsibility
  25. for any problems which PBWiz may cause you.  If you do
  26. encounter a problem, please let me know about it, and I will do
  27. my best to verify and repair the error.
  28.  
  29. It is expected that if you find PBWiz useful, you will register
  30. your copy. You may not use PBWiz routines in programs intended
  31. for sale unless you have registered.  Registration entitles you
  32. to receive the latest version of PBWiz, complete with full
  33. source code in assembly language and BASIC.  The assembly code
  34. is designed for the MASM 6.0 assembler and may require minor
  35. modifications if you wish to use it with OPTASM, TASM, or
  36. earlier versions of MASM.
  37.  
  38. Warning: Unregistered use of PBWiz for more than 30 days may
  39. encourage the author to sing Gregorian chants under your
  40. window.  Don't let this happen to you!
  41.  
  42.                        Table of Contents                 page 2
  43.  
  44.  
  45.  
  46.  Synopsis and Legal Info .................................... 1
  47.  
  48.  Table of Contents .......................................... 2
  49.  
  50.  Overview ................................................... 3
  51.  
  52.  Archives ................................................... 4
  53.  
  54.  Dates and Times ............................................ 6
  55.  
  56.  Equipment Info ............................................. 7
  57.  
  58.  Extended Math ............................................. 11
  59.  
  60.  Graphics .................................................. 14
  61.  
  62.  Keyboard .................................................. 17
  63.  
  64.  Memory (EMS) .............................................. 21
  65.  
  66.  Memory (XMS) .............................................. 24
  67.  
  68.  Mouse Support ............................................. 26
  69.  
  70.  Strings ................................................... 29
  71.  
  72.  Text-mode Video ........................................... 32
  73.  
  74.  Credits ................................................... 36
  75.  
  76.                            Overview                      page 3
  77.  
  78.  
  79.  
  80. Any program that uses any of the PBWiz routines must DECLARE
  81. them appropriately.  To make this easy, I've created a single
  82. file which contains all of the necessary declarations.  Put
  83. the following line at the top of your program to use it:
  84.  
  85.    $INCLUDE "PBWIZ.INC"
  86.  
  87. The DECLARE statements contained in this file tell PowerBASIC
  88. how to behave when it runs into any PBWiz routines.  They
  89. don't actually load the routines.  To do that, you must $LINK
  90. the appropriate units.  The names of the units to $LINK are
  91. specified in each chapter of this manual.
  92.  
  93. Each chapter covers a specific unit or pair of units.  If the
  94. code is written in assembly language, the unit name ends in A.
  95. If the code is written in PowerBASIC, the unit name ends in B.
  96. So, for instance, the VIDEO units are called VIDEOA.OBJ and
  97. VIDEOB.PBU.  If the code for a chapter is written entirely in
  98. one language, I don't bother with -A or -B suffix.
  99.  
  100.                            Archives                      page 4
  101.  
  102.  
  103.  
  104. When I started in the microcomputer industry, there was a
  105. small variety of file archivers, all (more or less)
  106. compatible.  They did not provide compression, which was
  107. relegated to another large selection of more-or-less
  108. compatible utilities.  Then came SEA's ARC.  It was very slow,
  109. but it did compression as well as archiving, and included CRC
  110. checks so you could know whether the files were intact.  It
  111. swept the BBS scene in short order, becoming the new
  112. standard.  A few other archivers competed on about a level
  113. footing, providing only minor variances on the ARC theme. Then
  114. SEA decided to sue one of its more successful competitors,
  115. Phil Katz (PKARC).  The end result was the ZIP standard... but
  116. in the chaos resulting from the breaking of the ARC standard,
  117. many other archivers came into being: ARJ, LZH, PAK, ZOO, and
  118. so forth.
  119.  
  120. PBWiz helps resolve the confusion by providing a single set of
  121. routines which allow you to view the contents of archives in
  122. any of the above-mentioned formats: ARC, ARJ, LZH, PAK, ZIP, or
  123. ZOO.  Only archive directories are provided at this time.
  124. Other formats will also be added as they arise.  If you have
  125. details on the format of an archive that you'd like me to add,
  126. please send them my way.  I'll do what I can to get it into
  127. PBWiz.
  128.  
  129. This unit requires the STRING unit (discussed later) as well.
  130. To use it in your program, you need to include both units:
  131.  
  132.    $LINK "stringa.obj"
  133.    $LINK "stringb.pbu"
  134.    $LINK "archives.pbu"
  135.  
  136. Viewing archive directories is handled in roughly the same
  137. fashion as you might view a DOS file directory.  This makes it
  138. possible to treat an archive and a subdirectory in a similar
  139. manner.
  140.  
  141. When you're looking for the first file in an archive, use the
  142. FindFirstA function.  You must specify the archive name and a
  143. file name.  The archive name may include a drive and path
  144. specification, and does not need to have the archive
  145. extension.  If you leave off the extension, FindFirstA will use
  146. the first archive it comes across that matches the rest of the
  147. specification. Note that the archive specification may not
  148. contain wildcards.  In contrast, the search file name may not
  149. contain drive or path specs, but may contain wildcards.
  150.  
  151.    CALL FindFirstA (Archive$, Filename$, ErrCode%)
  152.  
  153.                            Archives                      page 5
  154.  
  155.  
  156.  
  157. If there are no files to be found, or if the archive
  158. specification was bad, an error code will be returned.  If
  159. there was no error, there may well be more files to be found.
  160. You can find each of them with FindNextA:
  161.  
  162.    CALL FindNextA (ErrCode%)
  163.  
  164. Of course, just finding a matching file doesn't do you much
  165. good unless you can retrieve information about it.  You can use
  166. any of the following routines to provide information about a
  167. matched file:
  168.  
  169.    Nam$ = GetNameA$
  170.    Dat$ = GetDateA$
  171.    Tim$ = GetTimeA$
  172.    CRC$ = GetCRCA$
  173.    StorageMethod$ = GetStoreA$
  174.  
  175.    CALL GetSizeA (OriginalSize&, CurrentSize&)
  176.  
  177. When you're done viewing an archive, be sure to close it:
  178.  
  179.    CALL CloseA
  180.  
  181. Let's try an example.  Given that you've already written the
  182. $LINK line as specified on the previous page, you could see
  183. all of the files in an archive using a program like this:
  184.  
  185.    CALL FindFirstA (Archive$, "*.*", ErrCode%)
  186.    DO UNTIL ErrCode%
  187.       PRINT GetNameA$
  188.       CALL FindNextA (ErrCode%)
  189.    LOOP
  190.    CALL FCloseA
  191.  
  192. This program fragment also assumes that you have set Archive$
  193. to the name of an archive.  It might be convenient to set it to
  194. the command line for testing purposes:
  195.  
  196.    Archive$ = UCASE$(LTRIM$(RTRIM$(COMMAND$)))
  197.  
  198.                         Dates and Times                  page 6
  199.  
  200.  
  201.  
  202. This unit allows you to validate and compare dates.  It also
  203. provides the day of the week, given the date.  Dates may not be
  204. before the year 1900.  Date strings may be in the form
  205. "01/01/91" or "01-01-1991" (the delimiter is not significant
  206. and years may be two or four digits; two-digit years will be
  207. assumed to be in the 20th century).
  208.  
  209. To use the routines in this unit, include the following line
  210. at the top of your program:
  211.  
  212.    $LINK "timedate.pbu"
  213.  
  214. Let's start off with date validation.  It's often important to
  215. know if a date entered into your program is a valid date.
  216.  
  217.    IF GoodDate%(DateSt$) THEN PRINT "The date is valid."
  218.  
  219. It can also be helpful to know on which day of the week a given
  220. date falls.
  221.  
  222.    Day$ = WeekDay$(DateSt$)
  223.  
  224. There are many useful things you can accomplish by turning a
  225. date into a number which represents that date (or vice versa).
  226. This allows you to compare two dates, which is important if you
  227. want to sort by date; find out what the date will be in a given
  228. number of days, or what it was some number of days ago; find
  229. the number of days between two dates; display a calendar; and
  230. so forth.  This is easy to do with PBWiz:
  231.  
  232.    DateNr& = Date2Num&(DateSt$)
  233.  
  234.    DateSt$ = Num2Date$(DateNr&)
  235.  
  236. The DateNr& represents the number of days since January 1,
  237. 1900.  This is less than 65,535 for dates that go up to around
  238. the year 2070 or so, so you may wish to store the dates in
  239. compressed two-byte form if your required range of dates is not
  240. that large:
  241.  
  242.    CrunchDate% = CVI(LEFT$(MKL$(DateNr&), 2))
  243.  
  244. This can be reversed simply:
  245.  
  246.    DateNr& = CVL(MKI$(CrunchDate%) + STRING$(2, 0))
  247.  
  248. Note that dates crunched this way are only useful for storage
  249. purposes, since the numbers greater than 32,767 are stored as
  250. negative numbers due to the signed integer format BASIC uses.
  251. You must uncompress them before doing any comparisons or date
  252. calculations.  Still, for a savings of 50%, it may be worth the
  253. hassle to convert back and forth.
  254.  
  255.                         Equipment Info                   page 7
  256.  
  257.  
  258.  
  259. The equipment unit gives you information about the computing
  260. environment. This includes both installed software and
  261. hardware.  You can use the equipment information routines by
  262. including this line in your program:
  263.  
  264.    $LINK "equipmen.obj"
  265.  
  266. The first function allows you to determine if an "enhanced"
  267. keyboard (101-key) is installed.  It may not be able to figure
  268. out what the keyboard is on some older not-quite-clone PCs, in
  269. which case it will take the safe way out and report that there
  270. is no enhanced keyboard.  This function returns -1 if there is
  271. an enhanced keyboard present, 0 if not.
  272.  
  273.    Enhanced% = KbdType%
  274.  
  275. Want to know the type of processor (CPU) being used?  Can do!
  276.  
  277.    CPU% = Processor%
  278.  
  279. The results will be reported as a number which can be decoded
  280. as follows:
  281.  
  282.    0    NEC V20
  283.    1    8088 or 8086
  284.    2    80186
  285.    3    80286
  286.    4    80386 or 80486
  287.  
  288. If anyone knows how to differentiate between an 80386 and an
  289. 80486, please let me know.  I've only seen one test which could
  290. do it, and it wasn't reliable.
  291.  
  292. Maybe you'd like to check for a CD-ROM drive:
  293.  
  294.    Drives% = CDROM%
  295.  
  296. This tells you how many logical drives exist, if there is a
  297. CD-ROM available. If not, it will return 0.  Note that the
  298. CD-ROM installation check conflicts with the GRAPHICS.COM
  299. installation check for DOS 4.0, due to some screw-up at IBM or
  300. Microsoft.  I'm not yet sure whether DOS 5.0 is similarly
  301. afflicted.
  302.  
  303. The number of floppy drives installed is retrieved like this:
  304.  
  305.    Drives% = Floppies%
  306.  
  307.                         Equipment Info                   page 8
  308.  
  309.  
  310.  
  311. There may be up to four floppy drives in a system; however, the
  312. AT CMOS data area only directly supports two. This makes it
  313. easy to find out what kind of drives the first two are, but not
  314. the second two.  Oh well, guess we'll have to settle for what
  315. we can get, right?
  316.  
  317.    CALL FloppyType (Drive1, Drive2)
  318.  
  319. The results from FloppyType are returned as follows:
  320.  
  321.    0    no drive
  322.    1    5 1/4"    360K
  323.    2    5 1/4"    1.2M
  324.    3    3 1/2"    720K
  325.    4    3 1/2"    1.44M
  326.  
  327. Result codes of 5-7 are available, but not yet defined.  One
  328. might guess that the 2.88M drive supported by DOS 5.0 will be
  329. drive type 5.  Has anybody seen one of those puppies yet?
  330.  
  331. New memory types sure have burgeoned over the years...
  332. expanded, extended, and now XMS.  There are routines to check
  333. all of these:
  334.  
  335.    BaseExt& = AllExtMem&     ' extended memory installed
  336.    NowExt& = GetExtM&        ' BIOS extended memory available
  337.  
  338.    CALL GetEMSm (TotalPages%, FreePages%)    ' expanded memory
  339.  
  340.    CALL GetXMSm (LargestFree&, TotalFree&)   ' XMS memory
  341.  
  342. When you're dealing with extended memory, whether it be
  343. BIOS-type or using the XMS standard, the results are returned
  344. in kilobytes.  Multiply 'em by 1024 to convert to bytes.  When
  345. you're dealing with expanded memory (EMS), the results are in
  346. pages of 16,384 bytes.
  347.  
  348. I might note, by the way, that Microsoft seems to have
  349. intentionally crippled the XMS standard.  It can only support a
  350. maximum of 64 megabytes.  This may seem like a lot now, but I
  351. can remember when my first PC (a Compaq portable) was the awe
  352. of the neighborhood with 384K RAM!  A few years down the road,
  353. the artificial limitations of XMS are going to be a nuisance.
  354.  
  355. A few more routines to get the versions of the EMS and XMS
  356. drivers, if any:
  357.  
  358.    CALL GetEMSv (MajorV%, MinorV%)
  359.    CALL GetXMSv (MajorV%, MinorV%)
  360.  
  361. These return the major and minor version numbers as two
  362. separate integers. For example, EMS 4.0 would return major
  363. version 4, minor version 0.
  364.  
  365.                         Equipment Info                   page 9
  366.  
  367.  
  368.  
  369. It's nice to know a little about the operating environment.
  370. With the below routines, you can find out what the DOS version
  371. is; what version of 4DOS, if any, is in use; and whether
  372. Microsoft Windows is running.
  373.  
  374.    CALL GetDOSv (MajorV%, MinorV%)
  375.    CALL Get4DOSv (MajorV%, MinorV%)
  376.    CALL WinCheck (MajorV%, MinorV%)
  377.  
  378. These return results as major and minor version numbers, as
  379. discussed on the previous page.  The Get4DOSv and WinCheck
  380. routines return zeroes if 4DOS and Windows, respectively, are
  381. not available.
  382.  
  383. There are a couple of curious features of GetDOSv to keep in
  384. mind.  If the version is 10 or higher, you're running in OS/2
  385. compatibility mode.  DOS version 10 is actually OS/2 1.0,
  386. version 20 is OS/2 2.0, and so on. Secondly, if you're using
  387. DOS 5.0, the version reported may not be 5.0-- DOS 5.0 can be
  388. told to reply with a lower version number to allow some older
  389. software (which checks for a specific DOS version) to run
  390. properly.
  391.  
  392. One final routine that should be of some value is the one that
  393. allows you to find out what kind of display is available.  It
  394. tells you the specific adapter and whether the display is color
  395. or monochrome.  There is one case in which it can be confused,
  396. however-- if the adapter is CGA, the display is assumed to be
  397. color, since there is no way for the computer to know any
  398. differently.  So, although this routine provides a good idea of
  399. what is available, it would be a good idea to provide an option
  400. to tell the program that a monochrome display is attached.
  401. Microsoft normally uses "/B" for this purpose, so that might be
  402. a good standard to stick with.
  403.  
  404.    CALL GetDisplay (Adapter%, Mono%)
  405.    IF Mono% THEN
  406.       PRINT "Monochrome monitor"
  407.    ELSE
  408.       PRINT "Color monitor"
  409.    END IF
  410.    SELECT CASE Adapter%
  411.       CASE 1: PRINT "MDA"
  412.       CASE 2: PRINT "Hercules"
  413.       CASE 3: PRINT "CGA"
  414.       CASE 4: PRINT "EGA"
  415.       CASE 5: PRINT "MCGA"
  416.       CASE 6: PRINT "VGA"
  417.    END SELECT
  418.  
  419.                         Equipment Info                  page 10
  420.  
  421.  
  422.  
  423. Aside from some of the oldest semi-clones, it's possible to
  424. find out what sort of machine you're using by looking at a
  425. specific data byte.  You can access this as follows:
  426.  
  427.    Machine% = PCType%
  428.  
  429. The result will need decoding.  Here are some known values:
  430.  
  431.    255   PC or XT
  432.    254   XT
  433.    253   PCjr
  434.    252   PC AT
  435.    251   XT
  436.    250   PS/2 Model 30
  437.    249   PC Convertible
  438.    248   PS/2 Model 70 or 80
  439.    154   Compaq Portable
  440.     45   Compaq Portable
  441.  
  442. Likewise, all but some of the oldest semi-clones maintain a
  443. BIOS date value which tells you how old the BIOS ROM is.  This
  444. can be retrieved with the following routine:
  445.  
  446.    BIOSdate$ = PCDate$
  447.  
  448. If your program is running on one of the rare old machines
  449. which don't maintain a valid BIOS date, this routine will
  450. return "No Date " instead of an actual date.
  451.  
  452. As far as a program is concerned, DR DOS is essentially the
  453. same as MS-DOS.  Still, it's always nice to know what sort of
  454. operating environment you have.  You can find out whether your
  455. program is running under DR DOS with the following function:
  456.  
  457.    IF DRDOS% THEN PRINT "It's DR DOS" ELSE PRINT "MS-DOS"
  458.  
  459.                          Extended Math                  page 11
  460.  
  461.  
  462.  
  463. The extended math unit provides an expression evaluator and
  464. extensions to BASIC's math.  The math extensions include
  465. hyperbolic and inverse trig functions, a few handy constants,
  466. conversions, and more.  You can use the new math routines by
  467. including these lines at the top of your program:
  468.  
  469.    $LINK "extmatha.obj"
  470.    $LINK "extmathb.pbu"
  471.  
  472. The expression evaluator allows you to find the result of an
  473. expression contained in a string.  Normal algebraic precedence
  474. is used, e.g. 4+3*5 evaluates to 19.  The usual numeric
  475. operators (*, /, +, -, ^) are supported (multiply, divide, add,
  476. subtract, and raise to a power).  Use of negative numbers is
  477. just fine, of course.  Parentheses for overriding the default
  478. order of operations are also supported.
  479.  
  480. You may use either a double asterisk ("**") or a caret ("^")
  481. symbols to indicate exponentiation.
  482.  
  483. To evaluate an expression, you pass it to the evaluator as a
  484. string.  You will get back either an error code or a
  485. single-precision result.  Try this example to see how the
  486. expression evaluator works:
  487.  
  488.    $STACK 4096
  489.    $INCLUDE "pbwiz.inc"
  490.    $LINK "extmath.pbu"
  491.    DO
  492.       INPUT "Expression? "; Expr$
  493.       IF LEN(Expr$) THEN
  494.          CALL Evaluate (Expr$, Result!, ErrCode%)
  495.          IF ErrCode% THEN
  496.             PRINT "Invalid expression.  Error = "; ErrCode%
  497.          ELSE
  498.             PRINT "Result: "; Result!
  499.          END IF
  500.       END IF
  501.    LOOP WHILE LEN(Expr$)
  502.    END
  503.  
  504. An expression evaluator adds convenience to any program that
  505. needs to accept numbers.  Why make someone reach for a
  506. calculator when number crunching is what a computer does best?
  507.  
  508. NOTE: The expression evaluator uses recursion and will require
  509. more than the default amount of stack space.  That's why the
  510. $STACK metacommand is used.
  511.  
  512.                          Extended Math                  page 12
  513.  
  514.  
  515.  
  516. The new math functions are pretty much self-explanatory, so
  517. I'll just list them here.  A few general notes are given on the
  518. next page.
  519.  
  520.    Result% = GCDI%(Nr1%, Nr2%)    ' greatest common denominator
  521.    Result% = RotateL%(Nr%, Count%) ' rotate left
  522.    Result% = RotateR%(Nr%, Count%) ' rotate right
  523.    Result% = ShiftL%(Nr%, Count%) ' shift left
  524.    Result% = ShiftR%(Nr%, Count%) ' shift right
  525.  
  526.    Result& = GCDL&(Nr1&, Nr2&)    ' greatest common denominator
  527.    Result& = RotateLL&(Nr&, Count%) ' rotate left
  528.    Result& = RotateRL&(Nr&, Count%) ' rotate right
  529.    Result& = ShiftLL&(Nr&, Count%) ' shift left
  530.    Result& = ShiftRL&(Nr&, Count%) ' shift right
  531.  
  532.    Result! = ArcCosHS!(Nr!)       ' inverse hyperbolic cosine
  533.    Result! = ArcSinHS!(Nr!)       ' inverse hyperbolic sine
  534.    Result! = ArcTanHS!(Nr!)       ' inverse hyperbolic tangent
  535.    Result! = ArcCosS!(Nr!)        ' arc cosine  (1 >= Nr >= -1)
  536.    Result! = ArcSinS!(Nr!)        ' arc sine    (1 >= Nr >= -1)
  537.    Result! = ErfS!(Nr!)           ' error function
  538.    Result! = FactS!(Nr%)          ' factorial
  539.    Result! = CotS!(Nr!)           ' cotangent
  540.    Result! = CscS!(Nr!)           ' cosecant
  541.    Result! = SecS!(Nr!)           ' secant
  542.    Result! = CosHS!(Nr!)          ' hyperbolic cosine
  543.    Result! = SinHS!(Nr!)          ' hyperbolic sine
  544.    Result! = TanHS!(Nr!)          ' hyperbolic tangent
  545.    Result! = Deg2RadS!(Nr!)       ' convert degrees to radians
  546.    Result! = Rad2DegS!(Nr!)       ' convert radians to degrees
  547.    Result! = Cent2Fahr!(Nr!)      ' centigrade to Fahrenheit
  548.    Result! = Fahr2Cent!(Nr!)      ' Fahrenheit to centigrade
  549.    Result! = Kg2Pound!(Nr!)       ' convert kilograms to pounds
  550.    Result! = Pound2Kg!(Nr!)       ' convert pounds to kilograms
  551.    Pi! = PiS!                     ' the constant "pi"
  552.    e! = eS!                       ' the constant "e"
  553.  
  554.    Result# = ArcCosHD#(Nr#)       ' inverse hyperbolic cosine
  555.    Result# = ArcSinHD#(Nr#)       ' inverse hyperbolic sine
  556.    Result# = ArcTanHD#(Nr#)       ' inverse hyperbolic tangent
  557.    Result# = ArcCosD#(Nr#)        ' arc cosine  (1 >= Nr >= -1)
  558.    Result# = ArcSinD#(Nr#)        ' arc sine    (1 >= Nr >= -1)
  559.    Result# = ErfD#(Nr#)           ' error function
  560.    Result# = FactD#(Nr%)          ' factorial
  561.    Result# = CotD#(Nr#)           ' cotangent
  562.    Result# = CscD#(Nr#)           ' cosecant
  563.    Result# = SecD#(Nr#)           ' secant
  564.    Result# = CosHD#(Nr#)          ' hyperbolic cosine
  565.    Result# = SinHD#(Nr#)          ' hyperbolic sine
  566.    Result# = TanHD#(Nr#)          ' hyperbolic tangent
  567.    Result# = Deg2RadD#(Nr#)       ' convert degrees to radians
  568.    Result# = Rad2DegD#(Nr#)       ' convert radians to degrees
  569.    Pi# = PiD#                     ' the constant "pi"
  570.    e# = eD#                       ' the constant "e"
  571.  
  572.                          Extended Math                  page 13
  573.  
  574.  
  575.  
  576. Like BASIC's trig functions, these trig functions expect the
  577. angle to be in radians.  Conversion functions are provided in
  578. case you prefer degrees.
  579.  
  580. Note that there is no ArcTanS! or ArcTanD# function for the
  581. simple reason that BASIC supplies an ATN function.
  582.  
  583. Constants are expressed to the maximum precision available.
  584.  
  585. If you are not familiar with variable postfix symbols, here's a
  586. brief summary:
  587.  
  588.    Symbol   Meaning             Range (approximate)
  589.    ------   --------            -------------------------------
  590.      %      integer             +- 32767
  591.      &      long integer        +- 2 * 10^9
  592.      !      single precision    +- 1 * 10^38
  593.      #      double precision    +- 1 * 10^308
  594.  
  595. See PowerBASIC's online help for further details.
  596.  
  597.                        Graphics Support                 page 14
  598.  
  599.  
  600.  
  601. I was rather surprised to find that PowerBASIC lacks support
  602. for one of the standard VGA modes-- SCREEN 13, the 320x200
  603. 256-color mode.  I immediately decided to add support for that
  604. mode: dots, lines, boxes, polygons, and (of course) text.
  605. While I was at it, I thought I'd add support for a nonstandard
  606. VGA mode: 360x480 in 256 colors.  This mode will work on almost
  607. any plain VGA system, although it might not work on some older
  608. not-quite-compatible setups. I'll be adding other modes as I go
  609. along, including SuperVGA modes.  For now, there are only two
  610. modes:
  611.  
  612.    13    320x200, 256 colors, 40x25 text, any VGA
  613.    N0    360x480, 256 colors, 45x60 text, almost any VGA
  614.  
  615. The graphics routines all use the same nomenclature: a G,
  616. followed by a mode number, followed by the specific name.  This
  617. generic naming convention is used so that this chapter can
  618. refer to all available modes.  For example, if I say "G#Color"
  619. and you're using mode 13, you'd actually use "G13Color" in your
  620. program.  Ok?
  621.  
  622. There are two sets of routines for each mode-- the ones written
  623. in ASM and the ones written in BASIC.  The file names for these
  624. will be suffixed with "A" for ASM and "B" for BASIC.  For
  625. instance, to use the SCREEN 13 routines, you would add the
  626. following at the top of your program:
  627.  
  628.    $LINK "g13a.obj"
  629.    $LINK "g13b.pbu"
  630.  
  631. The first thing you will always have to do is to put the screen
  632. into the proper mode.  This is done with the G#Mode routine:
  633.  
  634.    CALL G#Mode (Graphics%)
  635.  
  636. Use 0 to switch to text mode or any other value to switch to
  637. graphics mode.
  638.  
  639. One difference between BASIC and BasWiz is that, instead of
  640. each "draw" command requiring a color parameter as in BASIC,
  641. the PBWiz library provides a separate color command:
  642.  
  643.    CALL G#Color (Foreground%, Background%)
  644.  
  645. The "foreground" color is used by all graphics routines.  The
  646. background color is used by the G#Cls routine.  Both foreground
  647. and background colors are used in the G#Write and G#WriteLn
  648. routines.
  649.  
  650.                        Graphics Support                 page 15
  651.  
  652.  
  653.  
  654. Here is a list of the corresponding routines, first BASIC, then
  655. PBWiz (replace the "#" with the appropriate mode number):
  656.  
  657.    ' get the color of a specified point
  658.    colour% = POINT(x%, y%)
  659.    colour% = G#GetPel%(x%, y%)
  660.  
  661.    ' set the color of a specified point
  662.    PSET (x%, y%), colour%
  663.    CALL G#Color (colour%, backgnd%): CALL G#Plot (x%, y%)
  664.  
  665.    ' draw a line of a specified color
  666.    LINE (x1%, y1%) - (x2%, y2%), colour%
  667.    CALL G#Color (colour%, backgnd%)
  668.    CALL G#Line (x1%, y1%, x2%, y2%)
  669.  
  670.    ' draw a box frame of a specified color
  671.    LINE (x1%, y1%) - (x2%, y2%), colour%, B
  672.    CALL G#Color (colour%, backgnd%)
  673.    CALL G#Box (x1%, y1%, x2%, y2%, 0)
  674.  
  675.    ' draw a box of a specified color and fill it in
  676.    LINE (x1%, y1%) - (x2%, y2%), colour%, BF
  677.    CALL G#Color (colour%, backgnd%)
  678.    CALL G#Box (x1%, y1%, x2%, y2%, 1)
  679.  
  680.    ' clear the screen and home the cursor
  681.    CLS
  682.    CALL G#Cls
  683.  
  684.    ' get the current cursor position
  685.    Row% = CSRLIN: Column% = POS(0)
  686.    CALL G#GetLocate (Row%, Column%)
  687.  
  688.    ' set the current cursor position
  689.    LOCATE Row%, Column%
  690.    CALL G#Locate (Row%, Column%)
  691.  
  692.    ' display a string without a carriage return and linefeed
  693.    PRINT St$;
  694.    CALL G#Write (St$)
  695.  
  696.    ' display a string with a carriage return and linefeed
  697.    PRINT St$
  698.    CALL G#WriteLn (St$)
  699.  
  700. Note that PBWiz, unlike BASIC, allows both foreground and
  701. background colors for text in graphics mode.
  702.  
  703.                        Graphics Support                 page 16
  704.  
  705.  
  706.  
  707. If you need to print a number rather than a string, just use
  708. the BASIC function STR$ to convert it.  If you don't want a
  709. leading space, use this approach:
  710.  
  711.    St$ = LTRIM$(STR$(Number))
  712.  
  713. The PBWiz library has other routines which have no BASIC
  714. equivalent.  One allows you to get the current colors:
  715.  
  716.    CALL G#GetColor (Foreground%, Background%)
  717.  
  718. Circles and ellipses can be drawn with the Ellipse routine.
  719. This is similar to the BASIC CIRCLE statement.  You specify the
  720. center of the ellipse (X,Y), plus the X and Y radius values:
  721.  
  722.    CALL G#Ellipse (CenterX%, CenterY%, XRadius%, YRadius%)
  723.  
  724. A circle is an ellipse with a constant radius.  So, to draw a
  725. circle, just set both radius values to the same value.
  726.  
  727. As well as the usual points, lines, and ellipses, PBWiz also
  728. allows you to draw regular polygons: triangles, squares,
  729. pentagons, hexagons, and so on.
  730.  
  731.    CALL G#Polygon (X%, Y%, Radius%, Vertices%, Angle!)
  732.  
  733. The X% and Y% values represent the coordinates of the center of
  734. the polygon. The Radius% is the radius of the polygon (as if
  735. you were fitting it into a circle).  Vertices% is the number of
  736. angles (also the number of sides) for the polygon to have.
  737. Angle! specifies the rotation of the polygon, and is specified
  738. in radians.
  739.  
  740.                        Keyboard Control                 page 17
  741.  
  742.  
  743.  
  744. The keyboard is not a particularly exciting or glamorous
  745. device.  In fact, we tend to forget about it except when it
  746. gets in the way.  Sometimes it's a hardware problem-- squishy
  747. or clacking keys, or perhaps a commonly-used key placed in an
  748. out-of-the-way location.  Then again, sometimes it's the
  749. software that's the problem.  There are many aspects of
  750. keyboard control, not all of which are necessarily related to
  751. input.  This unit will let you handle the keyboard in ways you
  752. may not have realized were possible.  Better yet, it can help
  753. make keyboard control easier than the users of your programs
  754. dreamed possible.  It all starts with the one little line:
  755.  
  756.    $LINK "keyboard.obj"
  757.  
  758. Let's start out with keyboard output.  Yep, not input--
  759. output.  We can stuff up to 15 keys into the keyboard buffer.
  760. Why would we ever want to do this? Perhaps to allow your
  761. program to pop-up a TSR automatically, to start another program
  762. after your program ends, or for creating key macros.  You can
  763. enter extended key codes (such as function keys) by using
  764. CHR$(0) before the scan code.
  765.  
  766.    CALL TypeIn (St$)
  767.  
  768. The usual keyboard action is somewhat sluggish.  We can make it
  769. a lot crisper by changing the key repeat rate and the delay
  770. before repeating begins.  This will work on ATs, but not PC/XT
  771. systems.
  772.  
  773.    CALL SpeedKey (RepDelay%, RepRate%)
  774.  
  775. The delay may be 0-3 (1 by default):
  776.  
  777.    0      250 milliseconds
  778.    1      500 milliseconds
  779.    2      750 milliseconds
  780.    3        1 second
  781.  
  782. The repeat rate may be 0-31 (11 by default).  The larger the
  783. number, the slower the speed-- 0 is around 30 cps, and 31 is
  784. around 2 cps.
  785.  
  786. I generally prefer to have the keyboard cranked up to full
  787. speed, using RepDelay% and RepRate% both set to zero.  This may
  788. be a bit too zippy for some people.  Experiment with it to see
  789. what you like best.
  790.  
  791. Of course, there may be reasons to make keyboard repeat less
  792. sensitive instead!  That might be a good idea in programs
  793. written for small children, for example.  You can adjust the
  794. keyboard equally well in either direction.
  795.  
  796.                        Keyboard Control                 page 18
  797.  
  798.  
  799.  
  800. PowerBASIC allows you to control one of the keys which can
  801. interrupt your program, namely the Break key.  There's another
  802. dangerous key which PBWiz allows you to control-- the PrtSc
  803. (PrintScreen) key.  PrtSc may not seem like a hazard at first
  804. glance, but if it's pressed by accident with no printer ready,
  805. or in a graphics mode which PrtSc doesn't understand how to
  806. print, the results can be pretty messy.  So, we let you turn it
  807. off or back on:
  808.  
  809.    CALL SetPrtSc (PrtScON%)
  810.  
  811. Use 0 to turn it off or anything else to turn it back on.  If
  812. you turn off PrtSc, you MUST remember to turn it back on again
  813. before your program ends! Otherwise, an interrupt vector will
  814. point into nowhere, causing probable chaos the next time PrtSc
  815. is pressed.
  816.  
  817. Regardless of whether you've turned the PrtSc key off, you can
  818. print the screen yourself just as if PrtSc had been pressed:
  819.  
  820.    CALL PrintScreen
  821.  
  822. Now here's a strange one for you.  When IBM brought out the
  823. 101-key keyboard, called the "enhanced" keyboard, they did
  824. something bizarre to the BIOS.  They still allowed old keyboard
  825. calls to work, but they filtered out the new key codes so no
  826. one would see them.  This made sure that no one would be able
  827. to use the capabilities of the "enhanced" keyboard without
  828. rewriting their programs.  So, the keyboard has been around for
  829. years, and there are still few programs that even notice when
  830. you press F11.  PowerBASIC v2.1 does not support the enhanced
  831. keyboard at all.  Fortunately, PBWiz -does-.  You can find out
  832. if an enhanced keyboard is installed with the KbdType%
  833. function, which is in the Equipment unit.
  834.  
  835. If there is an enhanced keyboard installed, you can activate it
  836. like so:
  837.  
  838.    CALL SetEnhKbd (Enhanced%)
  839.  
  840. With enhanced keyboard support activated, all key requests that
  841. used the old services are translated to the new services.  So,
  842. SetEnhKbd affects INKEY$ and other BASIC functions as well as
  843. other PBWiz keyboard routines.  Note that you MUST deactivate
  844. enhanced keyboard support before ending your program.
  845. Otherwise, an interrupt vector will point into nowhere,
  846. probably causing a crash on the next keypress!
  847.  
  848.                        Keyboard Control                 page 19
  849.  
  850.  
  851.  
  852. Speaking of INKEY$, I have a neat little function for you.  It
  853. works like INKEY$, but it doesn't remove the key from the
  854. keyboard buffer:
  855.  
  856.    ky$ = ScanKey$
  857.  
  858. How is this handy?  Well, let's suppose you're writing
  859. something that will work like the DIR or TYPE commands in DOS.
  860. It will display what may be a long listing, which you'd like to
  861. be able to pause with Ctrl-S or cancel with Ctrl-C.  Trouble
  862. is, if you use INKEY$ and the user was using "type ahead" to
  863. store another command, you've just wiped out his command while
  864. looking for those control codes.  With ScanKey$, you can check
  865. the key nondestructively.
  866.  
  867. On the other hand, if you're about to request important input,
  868. you may not want to chance having it answered from results of
  869. the keyboard buffer-- could be that the user meant those keys
  870. for another purpose.  In that case, it's a good approach to
  871. clear out the keyboard buffer just before the input:
  872.  
  873.    CALL ClearKbd
  874.  
  875. No keyboard unit would be complete without a selection of
  876. routines to check the shift states and get or set the keyboard
  877. toggles.  Let's start with the toggles, which are so called
  878. because they get toggled from one state to another:
  879.  
  880.     PRINT "Caps Lock  : ";
  881.     IF CapsOn% THEN PRINT "ON" ELSE PRINT "OFF"
  882.     PRINT "Insert     : ";
  883.     IF InsertOn% THEN PRINT "ON" ELSE PRINT "OFF"
  884.     PRINT "Num Lock   : ";
  885.     IF NumOn% THEN PRINT "ON" ELSE PRINT "OFF"
  886.     PRINT "Scroll Lock: ";
  887.     IF ScrollOn% THEN PRINT "ON" ELSE PRINT "OFF"
  888.  
  889. You can also turn the toggles off or on.  It's courteous to
  890. restore the original toggle states once you end your program,
  891. so you might want to save the original values for that
  892. purpose.  Then again, I guess that doesn't apply if your
  893. program is designed for the specific purpose of setting the
  894. toggles!
  895.  
  896.    CALL SetCaps (CapsLock%)
  897.    CALL SetInsert (InsertKey%)
  898.    CALL SetNum (NumLock%)
  899.    CALL SetScroll (ScrollLock%)
  900.  
  901. Does anyone actually use ScrollLock for anything?  Just
  902. wondering...
  903.  
  904.                        Keyboard Control                 page 20
  905.  
  906.  
  907.  
  908. The shift keys are unique in many respects.  They don't return
  909. codes that can be detected with INKEY$ or stuffed into the
  910. keyboard buffer; several can be pressed at the same time; and
  911. they don't repeat.  You can detect 'em with PBWiz, at any rate:
  912.  
  913.    IF AltPress% THEN PRINT "An ALT key is pressed."
  914.    IF CtrlPress% THEN PRINT "A CTRL key is pressed."
  915.    IF ShiftPress% THEN PRINT "A SHIFT key is pressed."
  916.  
  917.    IF LAltPress% THEN PRINT "The LEFT ALT key is pressed."
  918.    IF LCtrlPress% THEN PRINT "The LEFT CTRL key is pressed."
  919.    IF LShiftPress% THEN PRINT "The LEFT SHIFT key is pressed."
  920.  
  921.    IF RAltPress% THEN PRINT "The RIGHT ALT key is pressed."
  922.    IF RCtrlPress% THEN PRINT "The RIGHT CTRL key is pressed."
  923.    IF RShiftPress% THEN PRINT "The RIGHT SHIFT key is pressed."
  924.  
  925. NOTE that LAltPress%, LCtrlPress%, RAltPress%, and RCtrlPress%
  926. are ONLY available for enhanced keyboards.  They will not
  927. return useful results on older keyboards.
  928.  
  929.                          Memory (EMS)                   page 21
  930.  
  931.  
  932.  
  933. This unit provides support for expanded memory.  It will work
  934. with older EMS and EEMS drivers as well as the current EMS 4.0
  935. standard, with the exception of one or two routines (as noted)
  936. which take advantage of new capabilities.
  937.  
  938. Expanded memory may be present on any type of computer, from
  939. 8088 PCs to 80486 ATs.  It usually comes as a hardware board
  940. with a software driver for older machines; on ATs, it may be
  941. implemented using only a software driver which converts it from
  942. extended memory.  Drivers have also been written which make a
  943. hard disk function as EMS memory.  This broad range of use
  944. makes EMS support invaluable to programs which need extra
  945. memory.  EMS can theoretically support up to 1 gigabyte of RAM,
  946. although the documented limit as of v4.0 was only 32 megabytes.
  947.  
  948. This unit is called EMS.  You can access it by including this
  949. line at the top of your program:
  950.  
  951.    $LINK "ems.obj"
  952.  
  953. Of course, the first thing you need to know is whether any EMS
  954. memory actually exists:
  955.  
  956.    IF EMSexists% THEN PRINT "EMS exists"
  957.  
  958. The EMS version may also be retrieved:
  959.  
  960.    CALL EMSver (MajorV%, MinorV%)
  961.  
  962. It would be a good idea to check the EMS version if you plan to
  963. use any features which are only available as of EMS 4.0, such
  964. as reallocation.
  965.  
  966. Besides a mere existence and version checks, you will want to
  967. know how much EMS is available:
  968.  
  969.    PRINT "Total EMS installed: "; EMStotal%
  970.    PRINT "Free EMS memory    : "; EMSfree%
  971.  
  972. If you actually tried the above two lines, you would get a pair
  973. of values which don't seem to mean much.  The trick is to
  974. multiply them by 16,384 to convert them to bytes.  This is
  975. because EMS memory is always accessed in pages of 16k bytes
  976. each.  Any time you are dealing with a quantity of EMS memory,
  977. the quantity will be specified as a number of pages.
  978.  
  979.                          Memory (EMS)                   page 22
  980.  
  981.  
  982.  
  983. Before we get into the mechanics of accessing EMS memory, I'd
  984. like to bring up an optional routine which can improve access
  985. speed.  It should not be used if your program accesses EMS
  986. using routines other than the ones included here in PBWiz.  If
  987. you only use these EMS routines, though, you will find that it
  988. makes some kinds of memory accesses faster.  Use 0 for normal
  989. (slow) mode.  Do not use optimization if you are using more
  990. than one EMS handle!
  991.  
  992.    CALL EMSopt (Fast%)
  993.  
  994. Ok, let's get down to the nitty gritty.  (Where did that
  995. expression come from, anyway?!)  When you allocate EMS memory,
  996. you specify the number of pages you want, which must be at
  997. least 1.  If the allocation is successful, you are returned a
  998. "handle" which you can use to access the allocated memory.
  999. Otherwise, you get back an error code.
  1000.  
  1001.    CALL EMSopen (Pages%, Handle%, ErrCode%)
  1002.  
  1003. There are a limited number of handles available under some EMS
  1004. drivers.  The EMS spec, as of v4.0, allowed for a maximum of
  1005. 255 handles, and it's not unusual for a driver to support only
  1006. 20 or so.  Bearing in mind that some of these handles may be
  1007. used up by other applications, such as RAMdisks and caches,
  1008. this really doesn't allow much leeway.  Try to use as few
  1009. handles as possible!  You may well need to store multiple
  1010. values in different areas of the memory allocated for a single
  1011. handle, rather than allocating a new area of memory for each
  1012. value.
  1013.  
  1014. Suppose you find you need more memory than you first
  1015. allocated?  Or maybe less memory?  Well, provided EMS 4.0 or
  1016. later is in use, you can reallocate the block:
  1017.  
  1018.    CALL EMSresize (Handle%, Pages%, ErrCode%)
  1019.  
  1020. There is no way to reallocate memory under older versions of
  1021. EMS.  About the best you could do in that case would be to
  1022. allocate a new area of the desired size, copy over the relevant
  1023. data from the old area, and then deallocate the original area.
  1024. Of course, this assumes that there is enough memory available
  1025. to hold both areas, at least temporarily.
  1026.  
  1027. When you are finished using EMS, you must be sure to return the
  1028. memory you allocated to the system.  It is IMPORTANT to do this
  1029. before ending your program.  Otherwise, the memory you
  1030. allocated will be "lost" until the next time you boot the
  1031. computer.  Return the memory for each handle as follows:
  1032.  
  1033.    CALL EMSclose (Handle%)
  1034.  
  1035.                          Memory (EMS)                   page 23
  1036.  
  1037.  
  1038.  
  1039. Hmmmm... we've covered EMS detection, status info, allocating
  1040. memory, freeing memory, resizing memory... what's missing
  1041. here?  Ah!  We haven't discussed how to actually access the
  1042. memory!
  1043.  
  1044. Accessing EMS memory is quite simple, but it involves a couple
  1045. of steps.  EMS is mapped into a 64k block in the low area of
  1046. memory (the area under 1M, which can be directly accessed).
  1047. Since a page is 16k, the EMS block can hold up to four pages at
  1048. a time.  To transfer data between normal memory and EMS memory,
  1049. you must map the appropriate page(s) of EMS into the EMS
  1050. block.  This is done like so:
  1051.  
  1052.     CALL EMSmap (Handle%, PPage%, VPage%)
  1053.  
  1054. The PPage% is the physical page, that is, the page number
  1055. within the EMS block (0-3).  The VPage% is the virtual page,
  1056. which is the number of a page within the EMS memory associated
  1057. with Handle%.
  1058.  
  1059. Once you've mapped the desired virtual page into a physical
  1060. page, it can be accessed using standard PowerBASIC memory
  1061. commands, such PEEK$ and POKE$. First, set the segment:
  1062.  
  1063.    DEF SEG=EMSseg&
  1064.  
  1065. The offset within a page may be 0-16,383.  To get the
  1066. appropriate offset within the EMS block, you must add the
  1067. offset of the page itself.  This may be calculated as follows:
  1068.  
  1069.    DataOffset& = OffsetWithinPage% + PageNumber% * 16384&
  1070.  
  1071. One final note: for best compatibility, it would be good to
  1072. avoid using physical page 3 (the last 16k of the EMS block).
  1073. Some EMS drivers don't handle this page with complete accuracy,
  1074. for technical reasons I'm not going to get into right now.
  1075.  
  1076.                          Memory (XMS)                   page 24
  1077.  
  1078.  
  1079.  
  1080. This unit provides support for XMS extended memory.  It won't
  1081. work with extended memory unless an XMS driver is present.
  1082.  
  1083. Extended memory is only present in AT-class computers.  It is
  1084. not available on older PCs.  An XMS driver must also be used.
  1085. XMS drivers are included with MS-DOS 5.0 and Windows 3.0, among
  1086. other things, so this is not a major limitation.  XMS can
  1087. address a maximum of 64 megabytes.
  1088.  
  1089. This unit is called XMS.  You access it by including this line
  1090. at the top of your program:
  1091.  
  1092.    $LINK "xms.obj"
  1093.  
  1094. The first thing to check is whether any XMS memory exists:
  1095.  
  1096.    IF XMSexists% THEN PRINT "XMS exists"
  1097.  
  1098. The XMS version may also be retrieved:
  1099.  
  1100.    CALL XMSver (MajorV%, MinorV%)
  1101.  
  1102. The amount of XMS memory available may be reported in either of
  1103. two different ways: total amount and largest available block.
  1104.  
  1105.    PRINT "Total XMS free    : "; XMStfree&
  1106.    PRINT "Largest free block: "; XMSlfree&
  1107.  
  1108. XMS memory is manipulated in terms of 1,024 byte blocks, so the
  1109. amount of free memory is reported in kilobytes.  Any time you
  1110. are dealing with a quantity of EMS memory, the quantity will be
  1111. specified as a number of 1K blocks, except as otherwise noted.
  1112.  
  1113.                          Memory (XMS)                   page 25
  1114.  
  1115.  
  1116.  
  1117. When you allocate XMS memory, you specify the number of
  1118. kilobytes that you want.  This may be 0-65535, in theory.
  1119. Dunno, I don't have 64M RAM <grin>. If the allocation is
  1120. successful, you are returned a "handle" which you can use to
  1121. access the allocated memory.  Otherwise, you get back an error
  1122. code.
  1123.  
  1124.    CALL XMSopen (KBytes&, Handle%, ErrCode%)
  1125.  
  1126. There are a limited number of handles available, although the
  1127. number can be controlled somewhat by a driver parameter.  It's
  1128. probably best to use as few handles as possible, to avoid
  1129. running out.  You may well want to store multiple values in
  1130. different areas of the memory allocated for a single handle,
  1131. rather than allocating a new area of memory for each value.
  1132.  
  1133. Suppose you find you need more memory than you first
  1134. allocated?  Or maybe less memory?  Just reallocate the block:
  1135.  
  1136.    CALL XMSresize (Handle%, KBytes&, ErrCode%)
  1137.  
  1138. When you are finished using XMS, you must be sure to return the
  1139. memory you allocated to the system.  It is IMPORTANT to do this
  1140. before ending your program.  Otherwise, the memory you
  1141. allocated will be "lost" until the next time you boot the
  1142. computer.  Return the memory for each handle as follows:
  1143.  
  1144.    CALL XMSclose (Handle%)
  1145.  
  1146. To transfer data between normal memory and XMS memory, you must
  1147. provide the segment and offset of the normal memory area (use
  1148. the PowerBASIC functions VARSEG and VARPTR to find the address
  1149. of a variable).  The position within XMS memory is specified as
  1150. a long-integer offset starting at zero.
  1151.  
  1152.     CALL XMSread (Handle%, Posn&, Bytes&, DSeg%, DOfs%)
  1153.  
  1154.     CALL XMSwrite (Handle%, Posn&, Bytes&, DSeg%, DOfs%)
  1155.  
  1156. Note that the Bytes& to transfer must be an EVEN NUMBER.  It is
  1157. not restricted to 64k, however, so you can transfer a great
  1158. deal of data with these routines.
  1159.  
  1160. The XMS spec guarantees a "reasonable" number of interrupt
  1161. windows during a transfer; however, it is possible that you
  1162. might experience some communications dropouts if you do large
  1163. transfers during high-speed telecommunications.  If this is
  1164. expected to happen, test it carefully.
  1165.  
  1166.                          Mouse Support                  page 26
  1167.  
  1168.  
  1169.  
  1170. The mouse unit provides full-featured mouse support.  You can
  1171. see if a mouse is available and how many buttons it has, get
  1172. the cursor position (either the current position or the
  1173. position at the last press or release of a specified button),
  1174. set the cursor position, change the cursor, set the mouse
  1175. range, get hardware information about the mouse, and so on.
  1176.  
  1177. This unit is called MOUSE, so you access it by including the
  1178. following line at the top of your program:
  1179.  
  1180.    $LINK "mouse.obj"
  1181.  
  1182. There are two unusual mouse modes to be aware of.  One is text
  1183. mode, which is mapped to a 640x200 virtual display.  So, to
  1184. convert the results to text format, you need to divide the
  1185. cursor position by eight and add one.  To convert from text
  1186. format, subtract one and multiply by eight.
  1187.  
  1188. The second unusual mode is 320x200 CGA mode, which is also
  1189. mapped to 640x200. To convert the coordinates to this mode,
  1190. divide X by two.  To convert from this mode, multiply the X
  1191. coordinate by two.
  1192.  
  1193. All other modes use the actual display coordinates instead of a
  1194. bizarro virtual screen.  Why the peculiar CGA and text modes?
  1195. Well, evidently Microsoft never thought there'd be any video
  1196. adapters besides MDA and CGA, and decided to create a single
  1197. virtual screen size that worked for all modes. Not a bad idea,
  1198. I guess, but rather shortsighted.  Oh well.
  1199.  
  1200. One other nuisance that you may run into is that the mouse
  1201. cursor can't be directly turned on or off.  A "cursor
  1202. visibility" count is maintained-- if the mouse cursor was
  1203. turned on twice, you'll need to turn it off twice before it
  1204. will actually disappear.
  1205.  
  1206. Before using the mouse, you must initialize it.  The
  1207. initialization routine also checks to make sure that a mouse is
  1208. installed and tells you how many buttons it has.  It's best to
  1209. initialize the mouse after setting the screen mode, so the
  1210. mouse driver understands what mode you're using.  Not all mouse
  1211. drivers support all screen modes, but you can reasonably expect
  1212. any current mouse driver to support MDA, CGA, EGA, and VGA.
  1213. Hercules graphics mode is rarely supported, as it must be set
  1214. through direct hardware access rather than the standard
  1215. techniques, so the mouse driver has little way of knowing that
  1216. you've changed the mode.
  1217.  
  1218. The mouse routines will work equally well with two-button or
  1219. three-button rodents.  The middle button functions will return
  1220. 0 with two-button mice.
  1221.  
  1222.                          Mouse Support                  page 27
  1223.  
  1224.  
  1225.  
  1226. I won't go into great detail on these routines, because they're
  1227. pretty much self-explanatory.  The mouse is a fairly easy
  1228. device to deal with.
  1229.  
  1230. You can initialize the mouse driver like so:
  1231.  
  1232.    Buttons% = MouseInit%
  1233.  
  1234. This returns the number of mouse buttons available.  If there
  1235. is no mouse, zero will be returned.  Initialize the mouse after
  1236. setting the screen mode.
  1237.  
  1238. You can make the mouse cursor visible or invisible.  It will
  1239. function just as well in either state.  See the previous page
  1240. for some quirks.
  1241.  
  1242.    CALL MouseShow       ' show the cursor
  1243.    CALL MouseHide       ' hide the cursor
  1244.  
  1245. There are many ways to get the mouse cursor position.  You can
  1246. get the current position, the position at which the mouse was
  1247. located when a particular button was pressed, or the position
  1248. when a button was released. If you choose a past position, you
  1249. can also find out how many presses or releases of the button
  1250. have taken place since you last checked.
  1251.  
  1252.    X% = MouseWhereX%                   ' current X coordinate
  1253.    Y% = MouseWhereY%                   ' current Y coordinate
  1254.  
  1255.    CALL MouseLClick (Count%, X%, Y%)   ' left presses & posn
  1256.    CALL MouseMClick (Count%, X%, Y%)   ' middle presses & posn
  1257.    CALL MouseRClick (Count%, X%, Y%)   ' right presses & posn
  1258.  
  1259.    CALL MouseLRelease (Count%, X%, Y%) ' left releases & posn
  1260.    CALL MouseMRelease (Count%, X%, Y%) ' middle releases & posn
  1261.    CALL MouseRRelease (Count%, X%, Y%) ' right releases & posn
  1262.  
  1263. If you'd prefer to find out which buttons are currently
  1264. pressed, no problem:
  1265.  
  1266.    Pressed% = MouseLButton%  ' whether left button is pressed
  1267.    Pressed% = MouseMButton%  ' whether middle button is pressed
  1268.    Pressed% = MouseRButton%  ' whether right button is pressed
  1269.  
  1270. Of course, you can also set the cursor location:
  1271.  
  1272.    CALL MouseLocate (X%, Y%)   ' set the mouse cursor position
  1273.  
  1274. The mouse cursor range can be restricted to a given area of the
  1275. screen.  This area is expressed by giving the upper left corner
  1276. and lower right corner of the rectangular area to which to
  1277. restrict the cursor.
  1278.  
  1279.    CALL MouseWindow (X1%, Y1%, X2%, Y2%)
  1280.  
  1281.                          Mouse Support                  page 28
  1282.  
  1283.  
  1284.  
  1285. There are a variety of cursor shapes available for graphics
  1286. mode:
  1287.  
  1288.     0    hourglass ("please wait, program is working" symbol)
  1289.     1    pointing arrow (default)
  1290.     2    pointing hand
  1291.     3    crosshair
  1292.     4    target (box in a box)
  1293.     5    grabbing hand
  1294.  
  1295. If you have ideas for more, let me know and I'll see what I can
  1296. do.  Cursor shapes are not unduly difficult to define.
  1297.  
  1298. The cursor image is set like so:
  1299.  
  1300.    CALL MouseCursorG (CursorNr%)
  1301.  
  1302.                             Strings                     page 29
  1303.  
  1304.  
  1305.  
  1306. One of the true strengths of BASIC lies in its powerful string
  1307. handling capability.  I'd be remiss if I didn't provide
  1308. extensions which improve it even further.  This unit can be
  1309. accessed by including this line at the top of your program:
  1310.  
  1311.    $LINK "stringa.obj"
  1312.    $LINK "stringb.pbu"
  1313.  
  1314. The simplest of the PBWiz string routines may seem somewhat
  1315. whimsical, but it has proven useful to me on several occasions
  1316. in the past.  It reverses the order of characters in a string.
  1317.  
  1318.    CALL Reverse (St$)
  1319.  
  1320. One of the places this has come in useful is in searching a
  1321. string from the end-- a reverse INSTR routine:
  1322.  
  1323.    CALL RInstr (MainSt$, SubSt$, Posn%)
  1324.  
  1325. Rather than returning the first occurrence of a substring
  1326. within a main string, it returns the last occurrence.  Another
  1327. handy string search allows you to search for various types of
  1328. characters, rather than a specific substring:
  1329.  
  1330.    CALL TInstr (MainSt$, Types%, Posn%)
  1331.  
  1332. The type(s) may be specified using any combination of the
  1333. following.  Just add them together.
  1334.  
  1335.     1    alphabetic
  1336.     2    numeric
  1337.     4    symbolic
  1338.     8    control
  1339.    16    graphics
  1340.    32    space
  1341.  
  1342. Since you can search for any specific types, you can also
  1343. readily invert the search to look for any characters that are
  1344. NOT of a given type or types:
  1345.  
  1346.    Types% = NOT Types%
  1347.  
  1348. This gives you complete control.  A typical use for TInstr
  1349. might be to clean up user input and make sure that it's valid.
  1350. It's also good for parsing.
  1351.  
  1352.                             Strings                     page 30
  1353.  
  1354.  
  1355.  
  1356. Another routine that is useful for cleaning up and parsing user
  1357. input is called Crunch.  It allows you to eliminate adjacent
  1358. duplicates of a character or list of characters.  One use for
  1359. this, for instance, would be to eliminate repeated spaces,
  1360. converting an input string from "*.*    *.BAK  /B" to a more
  1361. manageable "*.* *.BAK /B".
  1362.  
  1363.    Result$ = Crunch$(St$, CharList$)
  1364.  
  1365. There are a pair of routines that you'll find valuable if you
  1366. need to check the validity of a string.  These are designed to
  1367. be compatible with the Xmodem and Ymodem file transfer
  1368. protocols, so you can use them for error checking in
  1369. telecommunications as well.
  1370.  
  1371.    Chk% = CheckSum% (St$)
  1372.  
  1373.    CALL CRC16 (St$, HiCRC%, LoCRC%)
  1374.  
  1375. Another pair of string routines provide a simple encryption
  1376. and decryption system for text.  The method used is not
  1377. particularly secure but are very fast and will be adequate for
  1378. many purposes.  As always, it helps to use a long and/or
  1379. complex password.
  1380.  
  1381.    CALL Cipher (St$, Password$)
  1382.    CALL CipherP (St$, Password$)
  1383.  
  1384. Both of these routines will encipher text on the first
  1385. run-through and decipher on the second, so you can use the same
  1386. routine either to encrypt or decrypt a message.  They are
  1387. different in one respect: the encrypted result of Cipher may
  1388. contain control characters, so it can't be used in a plain
  1389. sequential-access file.  The CipherP routine does not allow use
  1390. of extended ASCII characters (CHR$(128) - CHR$(255)), as it
  1391. sets the high bit on each character after encrypting it.  This
  1392. causes the results of CipherP to be printable (and useful in
  1393. sequential-access files), although they will look very strange.
  1394.  
  1395. The strings are encrypted (or decrypted) in place.  This
  1396. provides a certain extra measure of security for encryption--
  1397. the original plaintext strings are not left floating around in
  1398. memory where someone might see them.
  1399.  
  1400. One function is as much a file manipulation routine as it is a
  1401. string function.  It allows you to compare a file name to a
  1402. file pattern (which may contain wildcards) to see if they
  1403. match.  Only bare filespecs are supported-- you may not use
  1404. drive or path specifications in the names.
  1405.  
  1406.    IF MatchFile% (Pattern$, Filename$) THEN PRINT Filename$
  1407.  
  1408.                             Strings                     page 31
  1409.  
  1410.  
  1411.  
  1412. The MatchFile function can be used in creating your own
  1413. DOS-style utilities: DIR, COPY, and so forth.  Besides the
  1414. usual "accept file if it matches" approach, it can also be used
  1415. to implement the opposite: "exclude file if it matches."  This
  1416. gives you more flexibility than DOS itself supplies.
  1417.  
  1418. The PowerBASIC compiler provides a very nice function called
  1419. Extract$.  This allows you to retrieve a substring running from
  1420. the left side of a main string to a specified character
  1421. delimiter.  Not bad, but it might be handy to be able to grab a
  1422. numbered substring from any part of a main string, and to be
  1423. able to use a substring delimiter.  For instance, you might
  1424. load a record from a database which contains an address, where
  1425. each line is delimited by a carriage return and linefeed.
  1426. Rather than mucking around with Extract$, which really wasn't
  1427. designed with that sort of thing in mind, you'd do better to
  1428. use the PBWiz function called DelimExtract$:
  1429.  
  1430.    SubSt$ = DelimExtract$(St$, Delimiter$, Index%)
  1431.  
  1432. The index starts at 1 with the first substring.  If you choose
  1433. the index of a substring which doesn't actually exist, a null
  1434. string will be returned.
  1435.  
  1436.                         Text-mode Video                 page 32
  1437.  
  1438.  
  1439.  
  1440. Routines in this unit may be accessed if you include these
  1441. lines at the top of your program:
  1442.  
  1443.    $LINK "videoa.obj"
  1444.    $LINK "videob.pbu"
  1445.  
  1446. The graphical interface has become a "sexy" thing to have
  1447. these days, but there are still many good reasons to work in
  1448. text mode.  It's relatively fast, it works on all monitors,
  1449. and it's often the most appropriate choice.  Graphics is
  1450. overkill for many applications.  Besides, you can't redirect
  1451. graphics to a file or to the printer.
  1452.  
  1453. Come to think of it, text displayed by the PRINT statement
  1454. can't be redirected either.  Fortunately, we can fix that.
  1455. All it takes is sending the output through DOS:
  1456.  
  1457.    CALL DOSPrint (St$)   ' print to the current output device
  1458.  
  1459. A nice thing about DOS output is that ANSI display codes will
  1460. work if you have an ANSI driver (such as ANSI.SYS) installed.
  1461. If you're working with existing text, such as captured output
  1462. from a BBS or from an ANSI art program like TheDraw, you can
  1463. just use the DOSPrint routine to handle it.  If you're looking
  1464. to do your own ANSI output, though, there's an easier way than
  1465. looking up the individual codes and sending 'em out one at a
  1466. time.  The following routines send the appropriate ANSI codes
  1467. to the current DOS output device:
  1468.  
  1469.    CALL DOSCls                      ' clear the screen
  1470.    CALL DOSColor (Fore%, Back%)     ' set the screen colors
  1471.    CALL DOSLocate (Row%, Column%)   ' set the cursor position
  1472.  
  1473. The primary advantage of DOS output is that it can be
  1474. intercepted.  It normally goes to the screen, but you can
  1475. redirect it to a file, printer, or comm port, among other
  1476. things.  You can also be sure that DOS output will work
  1477. reasonably in a multitasking environment like DESQview or
  1478. Windows, instead of messing up the screen.  The disadvantage
  1479. is that DOS output is fairly slow.  It's great for command
  1480. line utilities, but not if you plan to do any fancy screen
  1481. work.  For that, you probably want direct-access techniques.
  1482.  
  1483. The direct screen access provided by PBWiz is rude, crude...
  1484. and faster than a greased euphemism.  It can't be redirected,
  1485. doesn't handle control codes, and won't even update the cursor
  1486. position.  In return for this lack of amenities, it gives you
  1487. two very valuable things: complete control and raw speed.
  1488.  
  1489.                         Text-mode Video                 page 33
  1490.  
  1491.  
  1492.  
  1493. The direct-access replacement for PRINT works like so:
  1494.  
  1495.    CALL XQPrint (St$, Row%, Column%, Attr%)
  1496.  
  1497. Now, you might guess that St$ is the text to print, and Row%
  1498. and Column% are where to print it.  The Attr% may seem a bit
  1499. more opaque.  The Attr% is the color to use-- actually, both
  1500. the foreground and background colors.  These are combined into
  1501. a single value because that's the way the display controller
  1502. wants to see it.  Remember, the emphasis here is on speed, not
  1503. necessarily convenience!  So how do you calculate an attribute
  1504. given the foreground and background colors?
  1505.  
  1506.    Attr% = CalcAttr% (Fore%, Back%)
  1507.  
  1508. An Attr% value is never less than 0 or greater than 255-- it
  1509. will fit into a single byte.  That may be useful to know for
  1510. storage purposes.  In any event, keep the Attr% in mind,
  1511. because we'll be seeing more of it in the future.  By the way,
  1512. you can unpack an attribute into foreground and background
  1513. colors too, if need be:
  1514.  
  1515.    CALL UnCalcAttr (Attr%, Fore%, Back%)
  1516.  
  1517. The direct-access routines allow for one, and only one, option
  1518. which might slow them down.  Way back in the dawn of time,
  1519. when IBM brought out the CGA, they knew that software was
  1520. cheap and hardware expensive.  Besides, no one would be so
  1521. rash as to bypass the BIOS and DOS routines provided by IBM,
  1522. so who would notice?  Well... of course, it didn't work out
  1523. that way.  The BIOS and DOS display routines are far too slow
  1524. and cumbersome for many purposes.  However, the damage was
  1525. done-- the IBM CGA, and many (though not all) CGAs since then,
  1526. flickers horribly if you access its display memory directly.
  1527. There is a software fix, but it slows the display down
  1528. tremendously.  Still, if your program is run on such a CGA,
  1529. you'll want to stop the flickering.  This may be done so:
  1530.  
  1531.    CALL AntiSnow (Slow%)   ' any nonzero value to stop flicker
  1532.  
  1533. You should provide a command-line switch or configuration
  1534. option to force anti-flicker support.  Don't do it by default,
  1535. as the slowdown is very noticeable.
  1536.  
  1537. The "menu" approach has become the standard way of allowing a
  1538. user to choose between various program options.  There are
  1539. many ways of designing a menu, but they almost always require
  1540. a "highlight" to show the current choice.  This highlight is
  1541. generally handled by changing the color of the chosen item:
  1542.  
  1543.    CALL ReColorArea (TopRow%, LftCol%, BotRow%, RtCol%, Attr%)
  1544.  
  1545.                         Text-mode Video                 page 34
  1546.  
  1547.  
  1548.  
  1549. Pop-up windows have become ubiquitous.  Naturally, PBWiz
  1550. supports them too:
  1551.  
  1552.    CALL PopWindow (TRow%, LCol%, BRow%, RCol%, Frame%,
  1553.       Attr%, Grow%, Shade%, TFore%, Title$)   ' use one line!
  1554.  
  1555. The first four parameters specify the upper left corner and
  1556. lower right corner of the window.  The window frame, if any,
  1557. is created just outside these coordinates.  If you choose a
  1558. shadow for a 3D effect, that will extend further outside the
  1559. window.  Keep this in mind if you want to save the part of the
  1560. screen under the window... but we'll get to that!  Let's see
  1561. what options are available for the frame type:
  1562.     0   no frame
  1563.     1   single lines
  1564.     2   double lines
  1565.     3   single horizontal, double vertical lines
  1566.     4   double horizontal, single vertical lines
  1567.     5   block graphic lines
  1568.  
  1569. These are the available shadows:
  1570.    -3   transparent shadow on the right
  1571.    -2   transparent shadow on the left
  1572.    -1   solid black shadow on the left
  1573.     0   no shadow
  1574.    1+   shadow attribute (use CalcAttr) for a colored shadow
  1575.  
  1576. Options for growing windows are as follows:
  1577.    -1   grow as fast as possible
  1578.     0   pop onto the screen
  1579.    1+   grow with a specified delay in milliseconds
  1580.         (15 works well for me)
  1581.  
  1582. The TFore% parameter is the foreground color to use for the
  1583. title (Title$), if any (use "" for no title).
  1584.  
  1585. It is worth noting that the "milliseconds" value is only
  1586. rather approximate.  The delay is based on the video card, and
  1587. is fairly similar on any computer system, but there will be a
  1588. noticeable difference between (say) an XT with a CGA and a 486
  1589. with a VGA.  Still, it's a useful delay-- if not really
  1590. precise, it's at least fairly accurate, and it has a fine
  1591. resolution.  You can access this delay yourself:
  1592.  
  1593.    CALL DelayV (MilliSeconds%)
  1594.  
  1595.                         Text-mode Video                 page 35
  1596.  
  1597.  
  1598.  
  1599. I mentioned screen saves a bit earlier.  With PBWiz, you can
  1600. save any part of a screen, and restore it later to the same
  1601. place or an entirely different area.  This also offers the
  1602. possibility of creating a screen image, storing it in a file,
  1603. and reading it into your program, among other things.  It
  1604. works like this:
  1605.  
  1606.    Scr$ = ScreenSave$ (TopRow%, LftCol%, BotRow%, RtCol%)
  1607.  
  1608.    CALL ScreenRestore (Scr$, TopRow%, LftCol%)
  1609.  
  1610. It takes about 4K to store a full 80x25 text screen.  The
  1611. exact calculation is Bytes = Rows * Columns * 2 + 2, if you
  1612. care to figure it out yourself. You can also take advantage of
  1613. a PBWiz routine to handle it:
  1614.  
  1615.    Bytes% = CalcSize% (TopRow%, LftCol%, BotRow%, RtCol%) + 2
  1616.  
  1617. Finally, we are left with a series of routines which let you
  1618. scroll (or clear) any part of the screen:
  1619.  
  1620.    CALL ScrollDown (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1621.    CALL ScrollLeft (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1622.    CALL ScrollRight (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1623.    CALL ScrollUp (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1624.  
  1625. If you attempt to scroll zero times, or more times than there
  1626. are rows (or columns, depending on which way you scroll), the
  1627. specified area of the screen will be cleared.  The Attr% gives
  1628. the color to use on the cleared area of the screen.
  1629.  
  1630.                             Credits                     page 36
  1631.  
  1632.  
  1633.  
  1634. I'd like to thank Dave Navarro for letting me in on the world
  1635. of PowerBASIC. His assistance has been most valuable to me in
  1636. many respects.  Without him, this library would have taken much
  1637. longer to get off the ground or would perhaps not even exist.
  1638.  
  1639. I would also like to thank Spectra, publishers of PowerBASIC,
  1640. for sending me the evaluation copy of PowerBASIC which led to
  1641. my decision to write this library.
  1642.  
  1643.