home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_BAS / PBWIZ20.ZIP / PBWIZ.DOC < prev    next >
Text File  |  1993-12-29  |  93KB  |  2,414 lines

  1.                 The PowerBasic Wizard's Library
  2.                 =-----------------------------=
  3.                           Version 1.9
  4.  
  5.      PBWIZ  Copyright (c) 1991-1993  Thomas G. Hanlin III
  6.  
  7.  
  8.  
  9. This is the PowerBasic Wizard's Library, a collection of BASIC
  10. and assembly language routines for use with PowerBasic 3.0c by
  11. Spectra Publishing. This collection is protected by copyright,
  12. but may be distributed according to the following conditions:
  13.  
  14.    All PBWiz files must be distributed together in unmodified
  15.    form. No files may be removed or added.
  16.  
  17. YOU USE THIS LIBRARY AT YOUR OWN RISK. It has been tested by me
  18. on my own computer, but I will not assume any responsibility for
  19. any problems which PBWiz may cause you. If you encounter a
  20. problem, please let me know about it, and I will do my best to
  21. verify and repair the error.
  22.  
  23. Shareware is not free software. It operates on a "try before you
  24. buy" basis. It is expected that if you find PBWiz useful, you
  25. will register your copy. You may not use PBWiz routines in
  26. programs intended for distribution unless you have registered.
  27.  
  28. Registration entitles you to receive the latest version of
  29. PBWiz, complete with full source code in assembly language and
  30. BASIC. The assembly code is designed for the MASM 6.0 assembler
  31. and may require modifications for use with other assemblers. See
  32. the ORDER.FRM file for ordering information.
  33.  
  34.                        Table of Contents                  page 2
  35.  
  36.  
  37.  
  38.  Synopsis and Legal Info .................................... 1
  39.  
  40.  Table of Contents .......................................... 2
  41.  
  42.  Overview ................................................... 3
  43.  
  44.  Library Utilities .......................................... 4
  45.  
  46.  ANSI emulation ............................................. 5
  47.  
  48.  Archives ................................................... 6
  49.  
  50.  Dates and Times ............................................ 8
  51.  
  52.  Disk Directories ........................................... 9
  53.  
  54.  Equipment Info ............................................ 11
  55.  
  56.  Extended Math ............................................. 15
  57.  
  58.  Graphics .................................................. 19
  59.  
  60.  Joystick .................................................. 25
  61.  
  62.  Keyboard .................................................. 26
  63.  
  64.  Memory (EMS) .............................................. 30
  65.  
  66.  Memory (XMS) .............................................. 33
  67.  
  68.  Mouse Support ............................................. 35
  69.  
  70.  SoundBlaster .............................................. 38
  71.  
  72.  Strings ................................................... 41
  73.  
  74.  Telecommunications ........................................ 44
  75.  
  76.  Text-mode Video ........................................... 48
  77.  
  78.  Credits ................................................... 52
  79.  
  80.                            Overview                       page 3
  81.  
  82.  
  83.  
  84. Any program that uses any of the PBWiz routines must DECLARE
  85. them appropriately. To make this easy, I've created a single
  86. file which contains all of the necessary declarations. Put the
  87. following line at the top of your program to use it:
  88.  
  89.    $INCLUDE "pbwiz.inc"
  90.  
  91. The DECLARE statements contained in the PBWIZ.INC file tell
  92. PowerBasic how to behave when it runs into any PBWiz routines.
  93. As of PowerBasic 3.0c, DECLAREd routines are not included in
  94. your .EXE file unless you actually use them in your code-- so,
  95. you can conveniently $INCLUDE the entire PBWIZ.INC declaration
  96. file in your programs, at no cost in .EXE size or speed. If you
  97. use an earlier version of PowerBasic, this is unfortunately not
  98. the case, so be sure to DECLARE only the routines you need.
  99.  
  100. The DECLAREs just tell PowerBasic what routines to expect. You
  101. must also tell PowerBasic where to find the routines:
  102.  
  103.    $LINK "pbwiz.pbl"
  104.  
  105. It is no longer necessary to $LINK each unit separately, since
  106. PowerBasic 3.0 provides true libraries. A library is nothing
  107. more or less than a collection of units. Instead of having to
  108. remember which routine is in which unit, you just put the unit
  109. in a library, and let the linker figure it out. Look, Ma-- no
  110. hands! The ability to use libraries adds terrific power to the
  111. BASIC language. But of course, you expect power of PowerBasic!
  112.  
  113. Note that $LINK is only used in your main program. If you write
  114. units which use PBWiz, you must include the appropriate DECLAREs
  115. in each unit, but not $LINK. The $LINK metacommand goes in the
  116. main program which makes use of the unit, not in the unit
  117. itself.
  118.  
  119.                       Library Utilities                   page 4
  120.  
  121.  
  122.  
  123. The PBLIB.EXE librarian that comes with PowerBasic 3.0 provides
  124. most of the features you'd expect of a library utility. Its
  125. interactive design is convenient for light library handling. If
  126. you're using a serious library like PBWiz, however, you may find
  127. it inadequate. PBWiz comes with a set of utilities which extend
  128. PBLIB to be more useful with large libraries. These utilities
  129. act as a shell for PBLIB, feeding it appropriate values based on
  130. your input-- so they are perfectly compatible.
  131.  
  132. If you wish to update a library with new versions of routines
  133. that are already in the library, use LIBUPD:
  134.  
  135.    LIBUPD libname module
  136.  
  137. Here, "libname" is the name of the library. The .PBL extension
  138. is optional. The "module" is the name of the object file (.OBJ
  139. or .PBU) to update. You may list multiple modules, separated by
  140. spaces, and the module name(s) may contain wildcards. LIBUPD
  141. will delete existing modules by that name (regardless of
  142. extension) from the library before adding the current version of
  143. the module to the library. If you don't specify a module
  144. extension, LIBUPD will use .OBJ if available, or .PBU otherwise.
  145.  
  146. The LIBUPD utility can be used to add new modules to a library.
  147. However, if you are building a new library or are adding modules
  148. that you know are not already in an existing library, it is more
  149. efficient to use LIBADD, which works the same way:
  150.  
  151.    LIBADD libname module
  152.  
  153. You can also list the contents of a library in any of a number
  154. of ways, using LIBLIST:
  155.  
  156.    LIBLIST libname switch
  157.  
  158. The switch may come before or after the libname. Only one switch
  159. may be used at a time. The switch may be:
  160.  
  161.    /EXTERNALS      list externals (routines the module needs)
  162.    /MODULES        list modules
  163.    /PUBLICS        list publics (routines the module provides)
  164.    /RAW            list the info that PBLIB gives, unchanged
  165.  
  166. The switch may be abbreviated to as little as one character. If
  167. you choose /EXTERNALS or /PUBLICs, the result will be an
  168. alphabetized list, cross-referenced with the name of the module
  169. that contains the symbols. If you choose /MODULES, an
  170. alphabetized list of the names of the modules contained in the
  171. library will be provided. If you choose /RAW, you'll get the
  172. same results as you would by going directly through PBLIB.EXE.
  173. The name of the output file will be libname.LST.
  174.  
  175. These utilities create temporary files before the final output.
  176. You will get faster results if you set an environment variable
  177. TEMP to point to a RAMdisk.
  178.  
  179.                         ANSI Emulation                    page 5
  180.  
  181.  
  182.  
  183. The ANSI emulator allows you to display text with ANSI codes
  184. without any need for ANSI.SYS or other ANSI drivers.  All you
  185. have to do is replace any PRINT statements with a call to the
  186. ANSI emulator:
  187.  
  188.    PRINT St$;
  189.  
  190. turns into
  191.  
  192.    AnsiPrint St$
  193.  
  194. Note that this does not include a carriage return and linefeed.
  195. If you want one, you'll have to add it explicitly:
  196.  
  197.    St$ = St$ + CHR$(13, 10)
  198.    AnsiPrint St$
  199.  
  200. Other ASCII codes are supported as well, including CHR$(7)
  201. [bell], CHR$(8) [backspace], and CHR$(12) [formfeed].
  202.  
  203. For a list of ANSI display codes, see your DOS manual or check
  204. with your local BBS.
  205.  
  206.                            Archives                       page 6
  207.  
  208.  
  209.  
  210. When I started in the microcomputer industry, there was a small
  211. variety of file archivers, all (more or less) compatible. They
  212. did not provide compression, which was relegated to another
  213. large selection of more-or-less compatible utilities. Then came
  214. SEA's ARC. It was very slow, but it did compression as well as
  215. archiving, and included CRC checks so you could know whether the
  216. files were intact. It swept the BBS scene in short order,
  217. becoming the new standard. A few other archivers competed on
  218. about a level footing, providing only minor variances on the ARC
  219. theme. Then SEA decided to sue one of their more successful
  220. competitors, Phil Katz (PKARC). The end result was PKZIP, which
  221. totally blew ARC away-- a useful hint to companies that choose
  222. to litigate instead of innovate. In the chaos resulting from the
  223. breaking of the former ARC standard, many other archivers came
  224. into being: ARJ, LZH, PAK, ZOO, et al.
  225.  
  226. PBWiz helps resolve the confusion by providing a single set of
  227. routines which allow you to view the contents of archives in any
  228. of the above-mentioned formats: ARJ, LZH, PAK, ZIP, ZOO, and
  229. even the antique ARC protocol. It also handles self-extracting
  230. EXE files of the forms produced by ARJ, LHARC and ZIP2EXE. Only
  231. archive directories are provided at this time. Other formats
  232. will also be added as they arise. If you have details on the
  233. format of an archive that you'd like me to add to PBWiz, please
  234. send them my way.
  235.  
  236. Viewing archive directories is handled in roughly the same
  237. fashion as you might view a DOS file directory. This makes it
  238. possible to treat an archive and a subdirectory in a similar
  239. manner, as long as the archive doesn't contain archives itself.
  240.  
  241. When you're looking for the first file in an archive, use the
  242. FindFirstA function. You must specify the archive name and a
  243. file name. The archive name may include a drive and path
  244. specification, and does not need to have the archive extension.
  245. If you leave off the extension, FindFirstA will use the first
  246. archive it comes across that matches the rest of the
  247. specification. Note that the archive specification may not
  248. contain wildcards. In contrast, the search file name may not
  249. contain drive or path specs, but may contain wildcards.
  250.  
  251.    FindFirstA Archive$, Filename$, ErrCode%
  252.  
  253.                            Archives                       page 7
  254.  
  255.  
  256.  
  257. If there are no files to be found, or if the archive
  258. specification was bad, an error code will be returned. If there
  259. was no error, there may well be more files to be found. You can
  260. find each of them with FindNextA:
  261.  
  262.    FindNextA ErrCode%
  263.  
  264. Of course, just finding a matching file doesn't do you much good
  265. unless you can retrieve information about it. You can use any of
  266. the following routines to provide information about a matched
  267. file:
  268.  
  269.    Nam$ = GetNameA$
  270.    Dat$ = GetDateA$
  271.    Tim$ = GetTimeA$
  272.    CRC$ = GetCRCA$
  273.    StorageMethod$ = GetStoreA$
  274.  
  275.    GetSizeA OriginalSize&, CurrentSize&
  276.  
  277. When you're done viewing an archive, be sure to close it:
  278.  
  279.    CloseA
  280.  
  281. Let's try an example, to view all files in an archive:
  282.  
  283.    $INCLUDE "pbwiz.inc"
  284.    $LINK "pbwiz.pbl"
  285.    ' set Archive$ to the name of an archive here!
  286.    FindFirstA Archive$, "*.*", ErrCode%
  287.    DO UNTIL ErrCode%
  288.       PRINT GetNameA$
  289.       FindNextA ErrCode%
  290.    LOOP
  291.    CloseA
  292.  
  293. This program fragment assumes that you have set Archive$ to the
  294. name of an archive. It might be convenient to set it to the
  295. command line for testing purposes:
  296.  
  297.    Archive$ = UCASE$(LTRIM$(RTRIM$(COMMAND$)))
  298.  
  299.                         Dates and Times                   page 8
  300.  
  301.  
  302.  
  303. This unit allows you to validate and compare dates. It also
  304. provides the day of the week, given the date. Dates may not be
  305. before the year 1900. Date strings may be in the form "01/01/91"
  306. or "01-01-1991" (the delimiter is not significant and years may
  307. be two or four digits; two-digit years will be assumed to be in
  308. the 20th century).
  309.  
  310. Let's start off with date validation. It's often important to
  311. know if a date entered into your program is a valid date.
  312.  
  313.    IF GoodDate%(DateSt$) THEN PRINT "The date is valid."
  314.  
  315. It can also be helpful to know on which day of the week a given
  316. date falls.
  317.  
  318.    Day$ = WeekDay$(DateSt$)
  319.  
  320. There are many useful things you can accomplish by turning a
  321. date into a number which represents that date (or vice versa).
  322. This allows you to compare two dates, which is important if you
  323. want to sort by date; find out what the date will be in a given
  324. number of days, or what it was some number of days ago; find the
  325. number of days between two dates; display a calendar; and so
  326. forth. This is easy to do with PBWiz:
  327.  
  328.    DateNr& = Date2Num&(DateSt$)
  329.  
  330.    DateSt$ = Num2Date$(DateNr&)
  331.  
  332. The DateNr& represents the number of days since January 1, 1900.
  333. This is less than 65,535 for dates that go up to around the year
  334. 2070 or so, so you may wish to store the dates in compressed
  335. two-byte form if your required range of dates is not that large:
  336.  
  337.    SmallerDate?? = DateNr&
  338.  
  339. The ?? suffix indicates a word value, which is a 16-bit unsigned
  340. integer.
  341.  
  342.                        Disk Directories                   page 9
  343.  
  344.  
  345.  
  346. This unit lets you read disk directories and retrieve the same
  347. information the DIR command shows, plus the file attribute. A
  348. string buffer is used to allow recursive directory searching.
  349.  
  350. When you're looking for the first file in a directory, use the
  351. FindFirstF function. You must provide a search filespec, which
  352. may contain a drive and path specification and use wildcards.
  353. You must also provide a search attribute, which may be any
  354. combination of the following added together:
  355.  
  356.    Normal          0      (nothing special)
  357.    Read Only       1      file can be read, but not written to
  358.    Hidden          2      file is "invisible"
  359.    System          4      special DOS system file
  360.    Subdirectory   16      subdirectory
  361.    Archive        32      (used by some backup utilities)
  362.  
  363. Note that you will always get all files that match any of your
  364. search specs. For example, if your search attribute was 18,
  365. you'd get normal files, hidden files, normal subdirectories and
  366. hidden subdirectories. If you want to be more specific, you will
  367. have to test the file attribute of the resulting file.
  368.  
  369. You must provide a string buffer for the directory search
  370. routine. This buffer must be 64 characters long and should be
  371. initialized before each call to FindFirstF. By using different
  372. buffer strings, you can search more than one directory at a
  373. time, or perform recursive searches through directories.
  374.  
  375.    Buffer$ = SPACE$(64)
  376.    FindFirstF Buffer$, Filename$, ErrCode%
  377.  
  378. If there are no files to be found, or if the file specification
  379. was bad, an error code will be returned. If there was no error,
  380. there may well be more files to be found. You can find each of
  381. them with FindNextF:
  382.  
  383.    FindNextF Buffer$, ErrCode%
  384.  
  385. Of course, just finding a matching file doesn't do you much good
  386. unless you can retrieve information about it. You can use any of
  387. the following routines to provide information about a matched
  388. file:
  389.  
  390.    FilAttr% = GetAttrF%(Buffer$)       ' file attribute
  391.    FilName$ = GetNameF$(Buffer$)       ' file name
  392.    FilDate$ = GetDateF$(Buffer$)       ' file date
  393.    FilSize& = GetSizeF&(Buffer$)       ' file size
  394.    FilTime$ = GetTimeF$(Buffer$)       ' file time
  395.  
  396.                        Disk Directories                  page 10
  397.  
  398.  
  399.  
  400. The file attribute can be most readily decoded with a series of
  401. ANDs. For example, to test for a subdirectory, you'd use:
  402.  
  403.    IF (FilAttr% AND 16) THEN PRINT "subdirectory"
  404.  
  405. Let's put all these routines together and see what it takes to
  406. make a quick'n'dirty DIR-style utility.
  407.  
  408.    $INCLUDE "pbwiz.inc"
  409.    $LINK "pbwiz.pbl"
  410.    SearchName$ = COMMAND$  ' get search spec from command line
  411.    Buffer$ = SPACE$(64)    ' set up buffer
  412.    FindFirstF Buffer$, SearchName$, SearchAttr%, ErrCode%
  413.    DO UNTIL ErrCode%
  414.       PRINT GetNameF$(Buffer$), GetSizeF&(Buffer$),
  415.       PRINT GetDateF$(Buffer$), GetTimeF$(Buffer$),
  416.       FilAttr% = GetAttrF%(Buffer$)
  417.       IF (FilAttr% AND 1) THEN PRINT "Read Only  ";
  418.       IF (FilAttr% AND 2) THEN PRINT "Hidden  ";
  419.       IF (FilAttr% AND 4) THEN PRINT "System  ";
  420.       IF (FilAttr% AND 16) THEN PRINT "Subdirectory  ";
  421.       IF (FilAttr% AND 32) THEN PRINT "Backup  ";
  422.       PRINT
  423.       FindNextF ErrCode%
  424.    LOOP
  425.  
  426. Doesn't take much, does it? Now you can add disk directory
  427. handling to your program with a bare minimum of effort!
  428.  
  429.                         Equipment Info                   page 11
  430.  
  431.  
  432.  
  433. The equipment unit gives you information about the computing
  434. environment. This includes both installed software and hardware.
  435.  
  436. The first function allows you to determine if an "enhanced"
  437. keyboard (101-key) is installed. It may not be able to figure
  438. out what the keyboard is on some older not-quite-clone PCs, in
  439. which case it will take the safe way out and report that there
  440. is no enhanced keyboard. This function returns -1 if there is an
  441. enhanced keyboard present, 0 if not.
  442.  
  443.    Enhanced% = KbdType%
  444.  
  445. Want to know the type of processor (CPU) being used? Can do!
  446.  
  447.    CPU% = Processor%
  448.  
  449. The results will be reported as a number, as follows:
  450.  
  451.    0    NEC V20
  452.    1    8088 or 8086
  453.    2    80186
  454.    3    80286
  455.    4    80386
  456.    5    80486
  457.  
  458. You can also check for a numeric coprocessor:
  459.  
  460.    MathChip% = NumProc%
  461.  
  462. The results will be reported as a number, as follows:
  463.  
  464.    0    no numeric coprocessor
  465.    1    8087
  466.    2    80287
  467.    3    80387
  468.  
  469. A 486DX will probably return 3, whereas a 486SX will probably
  470. return 0-- if anyone can confirm this, please let me know.
  471.  
  472.                         Equipment Info                   page 12
  473.  
  474.  
  475.  
  476. Maybe you'd like to check for a CD-ROM drive:
  477.  
  478.    Drives% = CDROM%
  479.  
  480. This tells you how many logical drives exist, if there is a
  481. CD-ROM available. If not, it will return 0. Note that the CD-ROM
  482. installation check conflicts with the GRAPHICS.COM installation
  483. check for DOS 4.0, due to some screw-up at IBM or Microsoft.
  484.  
  485. The number of floppy drives installed is retrieved like this:
  486.  
  487.    Drives% = Floppies%
  488.  
  489. There may be up to four floppy drives in a system; however, the
  490. AT CMOS data area only directly supports two. This makes it easy
  491. to find out what kind of drives the first two are, but not the
  492. second two. Oh well, guess we'll have to settle for what we can
  493. get, right?
  494.  
  495.    FloppyType Drive1%, Drive2%
  496.  
  497. The results from FloppyType are returned as follows:
  498.  
  499.    0    no drive
  500.    1    5 1/4"    360K
  501.    2    5 1/4"    1.2M
  502.    3    3 1/2"    720K
  503.    4    3 1/2"    1.44M
  504.  
  505. Result codes of 5-7 are available, but not yet defined. One
  506. might guess that the new 2.88M drive will be given drive type 5.
  507. Has anybody seen one of those puppies yet?
  508.  
  509. New memory types sure have burgeoned over the years... expanded,
  510. extended, and now XMS. There are routines to check all of these:
  511.  
  512.    BaseExt& = AllExtMem&     ' extended memory installed
  513.    NowExt& = GetExtM&        ' BIOS extended memory available
  514.  
  515.    GetEmsM TotalPages%, FreePages%    ' expanded memory
  516.  
  517.    GetXmsM LargestFree&, TotalFree&   ' XMS memory
  518.  
  519. When you're dealing with extended memory, whether it be
  520. BIOS-type or using the XMS standard, the results are returned in
  521. kilobytes. Multiply 'em by 1024 to convert to bytes. When you're
  522. dealing with expanded memory (EMS), the results are in pages of
  523. 16,384 bytes.
  524.  
  525. I might note, by the way, that Microsoft seems to have
  526. intentionally crippled the XMS standard. It can only support a
  527. maximum of 64 megabytes. This may seem like a lot now, but I can
  528. remember when my first PC (a Compaq portable) was the awe of the
  529. neighborhood with 384K RAM! A few years down the road, the
  530. artificial limitations of XMS are going to be a nuisance.
  531.  
  532.                         Equipment Info                   page 13
  533.  
  534.  
  535.  
  536. Other routines get the EMS and XMS driver versions:
  537.  
  538.    GetEmsV MajorV%, MinorV%
  539.    GetXmsV MajorV%, MinorV%
  540.  
  541. These return the major and minor version numbers as two separate
  542. integers. For example, EMS 4.0 would return major 4, minor 0.
  543.  
  544. It's nice to know a little about the operating environment. With
  545. the below routines, you can get the DOS version; 4DOS version,
  546. if any; and whether Microsoft Windows is running.
  547.  
  548.    GetDOSv MajorV%, MinorV%
  549.    Get4DOSv MajorV%, MinorV%
  550.    WinCheck MajorV%, MinorV%
  551.  
  552. These return results as major and minor version numbers, as
  553. discussed on the previous page. The Get4DOSv and WinCheck
  554. routines return zeroes if 4DOS and Windows, respectively, are
  555. not available.
  556.  
  557. There are a couple of curious features of GetDOSv to keep in
  558. mind. If the version is 10 or higher, you're running in OS/2
  559. compatibility mode. DOS version 10 is actually OS/2 1.0, version
  560. 20 is OS/2 2.0, and so on. Secondly, if you're using DOS 5.0 or
  561. later, the reported version number may be lower-- DOS can be
  562. told to reply with a lower version number to allow some older,
  563. badly-designed software (which checks for a specific DOS
  564. version) to continue running.
  565.  
  566. One final routine that should be of some value is the one that
  567. allows you to find out what kind of display is available. It
  568. tells you the specific adapter and whether the display is color
  569. or monochrome. There is one case in which it can be confused,
  570. however-- if the adapter is CGA, the display is assumed to be
  571. color, since there is no way for the computer to know any
  572. differently. So, although this routine provides a good idea of
  573. what is available, it would be a good idea to provide an option
  574. to tell the program that a monochrome display is attached.
  575. Microsoft normally uses "/B" for this purpose, so that might be
  576. a good standard to stick with.
  577.  
  578.    GetDisplay Adapter%, Mono%
  579.    IF Mono% THEN
  580.       PRINT "Monochrome monitor"
  581.    ELSE
  582.       PRINT "Color monitor"
  583.    END IF
  584.    SELECT CASE Adapter%
  585.       CASE 1: PRINT "MDA"
  586.       CASE 2: PRINT "Hercules"
  587.       CASE 3: PRINT "CGA"
  588.       CASE 4: PRINT "EGA"
  589.       CASE 5: PRINT "MCGA"
  590.       CASE 6: PRINT "VGA"
  591.    END SELECT
  592.  
  593.                         Equipment Info                   page 14
  594.  
  595.  
  596.  
  597. Aside from some of the oldest semi-clones, it's possible to find
  598. out what sort of machine you're using by looking at a specific
  599. data byte. You can access this as follows:
  600.  
  601.    Machine% = PCType%
  602.  
  603. The result will need decoding. Here are some known values:
  604.  
  605.    255   PC or XT
  606.    254   XT
  607.    253   PCjr
  608.    252   PC AT
  609.    251   XT
  610.    250   PS/2 Model 30
  611.    249   PC Convertible
  612.    248   PS/2 Model 70 or 80
  613.    154   Compaq Portable
  614.     45   Compaq Portable
  615.  
  616. Likewise, all but some of the oldest semi-clones maintain a BIOS
  617. date value which tells you how old the BIOS ROM is. This can be
  618. retrieved with the following routine:
  619.  
  620.    BIOSdate$ = PCDate$
  621.  
  622. If your program is running on one of the rare old machines which
  623. don't maintain a valid BIOS date, this routine will return
  624. "No Date " instead of an actual date.
  625.  
  626. As far as a program is concerned, DR DOS is essentially the same
  627. as MS-DOS. Still, it's always nice to know about these things.
  628. You can find out whether your program is running under DR DOS
  629. with the following function:
  630.  
  631.    IF DRDOS% THEN PRINT "DR DOS" ELSE PRINT "MS-DOS"
  632.  
  633. The number of serial and parallel ports available can be readily
  634. obtained:
  635.  
  636.    PRINT "Serial ports: "; CommPorts%
  637.    PRINT "Parallel ports: "; PrtPorts%
  638.  
  639.                          Extended Math                   page 15
  640.  
  641.  
  642.  
  643. The extended math unit provides an expression evaluator and
  644. extensions to BASIC's math. The math extensions include
  645. hyperbolic and inverse trig functions, a few handy constants,
  646. conversions, and more.
  647.  
  648. The expression evaluator allows you to find the result of an
  649. expression contained in a string. Normal algebraic precedence is
  650. used, e.g. 4+3*5 evaluates to 19. The usual numeric operators
  651. (*, /, +, -, ^) are supported (multiply, divide, add, subtract,
  652. and raise to a power). Use of negative numbers is just fine, of
  653. course. Parentheses for overriding the default order of
  654. operations are also supported.
  655.  
  656. You may use either a double asterisk ("**") or a caret ("^")
  657. symbols to indicate exponentiation.
  658.  
  659. The constant PI is recognized, as are the following functions:
  660.    ABS    absolute value        INT    integer
  661.    ACOS   inverse cosine        LOG    natural log
  662.    ASIN   inverse sine          SIN    sine
  663.    ATAN   inverse tangent       SQR    square root
  664.    COS    cosine                TAN    tangent
  665.    FRAC   fraction
  666.  
  667. Functions should not be nested.
  668.  
  669. Trig functions expect angles in radians.
  670.  
  671. To evaluate an expression, you pass it to the evaluator as a
  672. string. You will get back either an error code or a
  673. single-precision result. Try this example to see how the
  674. expression evaluator works:
  675.  
  676.    $STACK 8192
  677.    $INCLUDE "pbwiz.inc"
  678.    $LINK "pbwiz.pbl"
  679.    DO
  680.       INPUT "Expression? "; Expr$
  681.       IF LEN(Expr$) THEN
  682.          Evaluate Expr$, Result!, ErrCode%
  683.          IF ErrCode% THEN
  684.             PRINT "Invalid expression.  Error = "; ErrCode%
  685.          ELSE
  686.             PRINT "Result: "; Result!
  687.          END IF
  688.       END IF
  689.    LOOP WHILE LEN(Expr$)
  690.    END
  691.  
  692. The expression evaluator uses recursion and requires more than
  693. the default amount of stack space, which is why the $STACK
  694. metacommand is used. See CALC.BAS for a working demo.
  695.  
  696.                          Extended Math                   page 16
  697.  
  698.  
  699.  
  700. The new math functions are pretty much self-explanatory, so I'll
  701. just list them here. General notes on assumptions and
  702. constraints are given at the end of this chapter.
  703.  
  704.    Result! = ArcCosHS!(Nr!)       ' inverse hyperbolic cosine
  705.    Result! = ArcSinHS!(Nr!)       ' inverse hyperbolic sine
  706.    Result! = ArcTanHS!(Nr!)       ' inverse hyperbolic tangent
  707.    Result! = ArcCosS!(Nr!)        ' arc cosine (1 >= Nr >= -1)
  708.    Result! = ArcCotS!(Nr!)        ' inverse cotangent
  709.    Result! = ArcCotHS!(Nr!)       ' arc hyperbolic cotangent
  710.    Result! = ArcCscS!(Nr!)        ' inverse cosecant
  711.    Result! = ArcCscHS!(Nr!)       ' inverse hyperbolic cosecant
  712.    Result! = ArcSecS!(Nr!)        ' inverse secant
  713.    Result! = ArcSecHS!(Nr!)       ' inverse hyperbolic secant
  714.    Result! = ArcSinS!(Nr!)        ' arc sine   (1 >= Nr >= -1)
  715.    Result! = CeilS!(Nr!)          ' smallest integer >= Nr
  716.    Result! = Cent2Fahr!(Nr!)      ' centigrade to Fahrenheit
  717.    Result! = CosHS!(Nr!)          ' hyperbolic cosine
  718.    Result! = CotHS!(Nr!)          ' hyperbolic cotangent
  719.    Result! = CotS!(Nr!)           ' cotangent
  720.    Result! = CscHS!(Nr!)          ' hyperbolic cosecant
  721.    Result! = CscS!(Nr!)           ' cosecant
  722.    Result! = Deg2RadS!(Nr!)       ' convert degrees to radians
  723.    e! = eS!                       ' the constant "e"
  724.    Result! = ErfS!(Nr!)           ' error function
  725.    Result! = FactS!(Nr%)          ' factorial
  726.    Result! = Fahr2Cent!(Nr!)      ' Fahrenheit to centigrade
  727.    Result! = FloorS!(Nr!)         ' largest integer <= Nr
  728.    Result! = Kg2Pound!(Nr!)       ' convert kilograms to pounds
  729.    Pi! = PiS!                     ' the constant "pi"
  730.    Result! = Pound2Kg!(Nr!)       ' convert pounds to kilograms
  731.    Result! = Rad2DegS!(Nr!)       ' convert radians to degrees
  732.    Result! = SecHS!(Nr!)          ' hyperbolic secant
  733.    Result! = SecS!(Nr!)           ' secant
  734.    Result! = SinHS!(Nr!)          ' hyperbolic sine
  735.    Result! = TanHS!(Nr!)          ' hyperbolic tangent
  736.  
  737.                          Extended Math                   page 17
  738.  
  739.  
  740.  
  741.    Result# = ArcCosD#(Nr#)        ' arc cosine  (1 >= Nr >= -1)
  742.    Result# = ArcCosHD#(Nr#)       ' inverse hyperbolic cosine
  743.    Result# = ArcCotD#(Nr#)        ' inverse cotangent
  744.    Result# = ArcCotHD#(Nr#)       ' arc hyperbolic cotangent
  745.    Result# = ArcCscD#(Nr#)        ' inverse cosecant
  746.    Result# = ArcCscHD#(Nr#)       ' inverse hyperbolic cosecant
  747.    Result# = ArcSecD#(Nr#)        ' inverse secant
  748.    Result# = ArcSecHD#(Nr#)       ' inverse hyperbolic secant
  749.    Result# = ArcSinD#(Nr#)        ' arc sine    (1 >= Nr >= -1)
  750.    Result# = ArcSinHD#(Nr#)       ' inverse hyperbolic sine
  751.    Result# = ArcTanHD#(Nr#)       ' inverse hyperbolic tangent
  752.    Result# = CeilD#(Nr#)          ' smallest integer >= Nr
  753.    Result# = CosHD#(Nr#)          ' hyperbolic cosine
  754.    Result# = CotD#(Nr#)           ' cotangent
  755.    Result# = CotHD#(Nr#)          ' hyperbolic cotangent
  756.    Result# = CscD#(Nr#)           ' cosecant
  757.    Result# = CscHD#(Nr#)          ' hyperbolic cosecant
  758.    Result# = Deg2RadD#(Nr#)       ' convert degrees to radians
  759.    e# = eD#                       ' the constant "e"
  760.    Result# = ErfD#(Nr#)           ' error function
  761.    Result# = FactD#(Nr%)          ' factorial
  762.    Result# = FloorD#(Nr#)         ' largest integer <= Nr
  763.    Pi# = PiD#                     ' the constant "pi"
  764.    Result# = Rad2DegD#(Nr#)       ' convert radians to degrees
  765.    Result# = SecD#(Nr#)           ' secant
  766.    Result# = SecHD#(Nr#)          ' hyperbolic secant
  767.    Result# = SinHD#(Nr#)          ' hyperbolic sine
  768.    Result# = TanHD#(Nr#)          ' hyperbolic tangent
  769.  
  770.                          Extended Math                   page 18
  771.  
  772.  
  773.  
  774.    Result% = GcdI%(Nr1%, Nr2%)     ' greatest common denominator
  775.    Result% = RotateL%(Nr%, Count%) ' rotate left
  776.    Result% = RotateR%(Nr%, Count%) ' rotate right
  777.    Result% = ShiftL%(Nr%, Count%)  ' shift left
  778.    Result% = ShiftR%(Nr%, Count%)  ' shift right
  779.  
  780.    Result& = GcdL&(Nr1&, Nr2&)      ' greatest common denominator
  781.    Result& = RotateLL&(Nr&, Count%) ' rotate left
  782.    Result& = RotateRL&(Nr&, Count%) ' rotate right
  783.    Result& = ShiftLL&(Nr&, Count%)  ' shift left
  784.    Result& = ShiftRL&(Nr&, Count%)  ' shift right
  785.  
  786. The ceiling and floor functions are generally used in rounding.
  787.  
  788. Like BASIC's trig functions, the PBWiz trig functions expect the
  789. angle to be in radians. Conversion functions are provided in
  790. case you prefer degrees.
  791.  
  792. "Arc" and "inverse" are used interchangeably (e.g., an arc sine
  793. is the same as an inverse sine).
  794.  
  795. Note that there is no ArcTanS! or ArcTanD# function for the
  796. simple reason that BASIC supplies an ATN function.
  797.  
  798. Constants are expressed to the maximum precision available.
  799.  
  800. If you are not familiar with variable postfix symbols, here's a
  801. brief summary:
  802.  
  803.    Symbol   Meaning             Range (approximate)
  804.    ------   --------            -------------------------------
  805.      %      integer             +- 32767
  806.      &      long integer        +- 2 * 10^9
  807.      !      single precision    +- 1 * 10^38
  808.      #      double precision    +- 1 * 10^308
  809.  
  810. See PowerBasic's online help for further details.
  811.  
  812.                        Graphics Support                  page 19
  813.  
  814.  
  815.  
  816. I was rather surprised to find that PowerBasic lacks support for
  817. one of the standard VGA modes-- SCREEN 13, the 320x200 256-color
  818. mode. I immediately decided to add support for that mode: dots,
  819. lines, boxes, polygons, and (of course) text. While I was at it,
  820. I thought I'd add support for a nonstandard VGA mode: 360x480 in
  821. 256 colors. This mode will work on almost any plain VGA system,
  822. although it might not work on some older not-quite-compatible
  823. setups. SuperVGA modes are also supported for adapters based on
  824. the popular Tseng 4000 chip set and for any VESA-compatible
  825. SVGA.
  826.  
  827.    13    320x200, 256 colors, 40x25 text, any VGA
  828.    N0    360x480, 256 colors, 45x60 text, almost any VGA
  829.    N5     ? x ? , 256 colors, ? x ? text, Tseng 4000 SVGA
  830.    GV     ? x ?,  ??? colors, ? x ? text, VESA compatibles
  831.  
  832. The graphics routines all use the same nomenclature: a G,
  833. followed by a mode number, followed by the specific name. This
  834. generic naming convention is used so that this chapter can refer
  835. to all available modes. For example, if I say "G#Color" and
  836. you're using mode 13, you'd actually use "G13Color" in your
  837. program. Ok?
  838.  
  839. There are some differences in how the various sets of graphics
  840. routines need to be initialized and shut down. I'll give you an
  841. outline of the differences here and follow it up with more
  842. detailed information in the case of the SVGA modes. The VESA
  843. SVGA mode, GV, should be handled like this:
  844.  
  845.    GGVInit              ' initialize VESA handler
  846.    GGVMode ModeNr%      ' enter graphics mode
  847.    ' *** your main program goes here ***
  848.    GGVDone              ' exit graphics mode
  849.  
  850. The Tseng 4000 SVGA mode, N5, also needs initialization. Its
  851. handling is closer to that of the other modes, however:
  852.  
  853.    GN5Init ModeNr%, PixelsWide%, PixelsHigh%
  854.    GN5Mode 1            ' enter graphics mode
  855.    ' *** your main program goes here ***
  856.    GN5Mode 0            ' exit graphics mode
  857.  
  858. For the other modes, no special initialization is required. You
  859. just use G#Mode with a non-zero value to enter graphics mode, or
  860. zero to restore text mode. The text mode is assumed to be the
  861. normal 80x25 color mode. If this would not be your choice, you
  862. can safely use the PowerBasic SCREEN statement to pick a mode
  863. that's more to your liking.
  864.  
  865.                        Graphics Support                  page 20
  866.  
  867.  
  868.  
  869. Here's an example for typical handling of one of the non-SVGA
  870. modes (in this case, mode 13):
  871.  
  872.    G13Mode 1            ' enter graphics mode
  873.    ' *** your main program goes here ***
  874.    G13Mode 0            ' exit graphics mode
  875.  
  876. The non-SVGA modes have their actual mode number built into the
  877. routines themselves. In the case of SVGA modes, the routines
  878. were designed for more flexibility, since SVGAs come in a wide
  879. variety of configurations.
  880.  
  881. In the case of the Tseng 4000 modes, N5, you specify the BIOS
  882. mode number, along with the screen resolution, when setting up
  883. the routines. Only 256-color modes are supported. The BIOS mode,
  884. width, height, and amount of video memory required to support
  885. the mode may be any of the following:
  886.  
  887.       &H2D     640 x 350    256k
  888.       &H2E     640 x 480    512k
  889.       &H2F     640 x 400    256k
  890.       &H30     800 x 600    512k
  891.       &H38    1024 x 768     1M
  892.  
  893. In the case of the VESA modes, GV, you specify the VESA mode
  894. number when setting up the routines. To provide the greatest
  895. amount of flexibility, VESA modes are not predefined. Rather
  896. than picking a specific mode number, you ask VESA which modes
  897. are available on the current computer, and pick the mode that
  898. best suits your needs from the choices offered.
  899.  
  900. First, it's wise to make sure VESA support is available:
  901.  
  902.    VesaVersion MajorV%, MinorV%
  903.  
  904. This routine returns the version number of the VESA driver,
  905. split into major and minor parts. If VESA isn't available, both
  906. numbers will be zero.
  907.  
  908. Determining which VESA modes are available works something like
  909. scanning a disk or archive directory. You start by requesting
  910. the first mode. Any of a variety of functions can be used to
  911. find out the mode resolution and colors, whether it's a text or
  912. monochrome mode, and so forth. If the mode is unsuitable or
  913. you'd like to check further, you request the next mode number.
  914. It will return a mode of -1 if there are no more modes.
  915.  
  916. A working demonstration of these routines is presented on the
  917. next page, and may also be found in the file VESAINFO.BAS.
  918.  
  919.                        Graphics Support                  page 21
  920.  
  921.  
  922.  
  923.    $INCLUDE "pbwiz.inc"
  924.    $LINK "pbwiz.pbl"
  925.    DEFINT A-Z
  926.  
  927.    VesaVersion MajorV, MinorV
  928.    IF MajorV = 0 AND MinorV = 0 THEN
  929.       PRINT "Sorry, but you do not have VESA support."
  930.       END
  931.    END IF
  932.  
  933.    VMode = VesaFindFirst
  934.    DO UNTIL VMode = -1
  935.       CLS
  936.       PRINT "VESA Mode : &H"; HEX$(VMode)
  937.       PRINT "Mode Type : ";
  938.       IF VesaIsText THEN PRINT "Text "; ELSE PRINT "Graphics ";
  939.       IF VesaIsMono THEN PRINT "(mono)" ELSE PRINT "(color)"
  940.       PRINT "Resolution:"; VesaScrWidth; "x"; VesaScrHeight;
  941.       IF VesaIsText THEN PRINT "chars" ELSE PRINT "pixels"
  942.       PRINT "Char Size :"; VesaChrWidth; "x"; VesaChrHeight
  943.       PRINT "Colors    :"; VesaColors&
  944.       PRINT
  945.       PRINT "-- press a key to view next available mode --"
  946.       VMode = VesaFindNext
  947.       DO
  948.       LOOP WHILE LEN(INKEY$)
  949.       DO
  950.          ky$ = INKEY$
  951.       LOOP UNTIL LEN(ky$)
  952.       IF ky$ = CHR$(27) OR ky$ = CHR$(3) THEN EXIT DO
  953.    LOOP
  954.    END
  955.  
  956.  
  957.  
  958. The VESA mode information functions can be summarized, so:
  959.  
  960.    VesaFindFirst%       get first available mode number
  961.    VesaFindNext%        get next available mode number
  962.  
  963.    VesaIsMono%          whether mode is mono (-1 yes, 0 no)
  964.    VesaIsText%          whether mode is text (-1 yes, 0 no)
  965.  
  966.    VesaScrHeight%       screen height (characters or pixels)
  967.    VesaScrWidth%        screen width (characters or pixels)
  968.  
  969.    VesaChrHeight%       character font height (pixels)
  970.    VesaChrWidth%        character font width (pixels)
  971.  
  972.    VesaColors&          colors (color numbers) in mode
  973.  
  974.                        Graphics Support                  page 22
  975.  
  976.  
  977.  
  978. One difference between BASIC and PBWiz is that, instead of each
  979. "draw" command requiring a color parameter as in BASIC, the
  980. PBWiz library provides a separate color command:
  981.  
  982.    G#Color Foreground%, Background%
  983.  
  984. The "foreground" color is used by all graphics routines. The
  985. background color is used by the G#Cls routine. Both foreground
  986. and background colors are used in by G#Write and G#WriteLn.
  987.  
  988. Here is a list of the corresponding routines, first BASIC, then
  989. PBWiz (replace the "#" with the appropriate mode number):
  990.  
  991.    ' get the color of a specified point
  992.    colour% = POINT(x%, y%)
  993.    colour% = G#GetPel%(x%, y%)
  994.  
  995.    ' set the color of a specified point
  996.    PSET (x%, y%), colour%
  997.    G#Color colour%, backgnd%
  998.    G#Plot x%, y%
  999.  
  1000.    ' draw a line of a specified color
  1001.    LINE (x1%, y1%) - (x2%, y2%), colour%
  1002.    G#Color colour%, backgnd%
  1003.    G#Line x1%, y1%, x2%, y2%
  1004.  
  1005.    ' draw a box frame of a specified color
  1006.    LINE (x1%, y1%) - (x2%, y2%), colour%, B
  1007.    G#Color colour%, backgnd%
  1008.    G#Box x1%, y1%, x2%, y2%, 0
  1009.  
  1010.    ' draw a box of a specified color and fill it in
  1011.    LINE (x1%, y1%) - (x2%, y2%), colour%, BF
  1012.    G#Color colour%, backgnd%
  1013.    G#Box x1%, y1%, x2%, y2%, 1
  1014.  
  1015.    ' clear the screen and home the cursor
  1016.    CLS
  1017.    G#Cls
  1018.  
  1019.    ' get the current cursor position
  1020.    Row% = CSRLIN: Column% = POS(0)
  1021.    G#GetLocate Row%, Column%
  1022.  
  1023.    ' set the current cursor position
  1024.    LOCATE Row%, Column%
  1025.    G#Locate Row%, Column%
  1026.  
  1027.    ' display a string without a carriage return and linefeed
  1028.    PRINT St$;
  1029.    G#Write St$
  1030.  
  1031.    ' display a string with a carriage return and linefeed
  1032.    PRINT St$
  1033.    G#WriteLn St$
  1034.  
  1035.                        Graphics Support                  page 23
  1036.  
  1037.  
  1038.  
  1039. If you need to print a number rather than a string, just use the
  1040. BASIC function STR$ to convert it. If you don't want a leading
  1041. space, use this approach:
  1042.  
  1043.    St$ = LTRIM$(STR$(Number))
  1044.  
  1045. The PBWiz library has other routines which have no BASIC
  1046. equivalent. One allows you to get the current colors:
  1047.  
  1048.    G#GetColor Foreground%, Background%
  1049.  
  1050. Circles and ellipses can be drawn with the Ellipse routine. This
  1051. is similar to the BASIC CIRCLE statement. You specify the center
  1052. of the ellipse (X,Y), plus the X and Y radius values:
  1053.  
  1054.    G#Ellipse CenterX%, CenterY%, XRadius%, YRadius%
  1055.  
  1056. A circle is an ellipse with a constant radius. So, to draw a
  1057. circle, just set both radius values to the same value.
  1058.  
  1059. As well as the usual points, lines, and ellipses, PBWiz also
  1060. allows you to draw regular polygons: triangles, squares,
  1061. pentagons, hexagons, and so on.
  1062.  
  1063.    G#Polygon X%, Y%, Radius%, Vertices%, Angle!
  1064.  
  1065. The X% and Y% values represent the coordinates of the center of
  1066. the polygon. The Radius% is the radius of the polygon (as if you
  1067. were fitting it into a circle). Vertices% is the number of
  1068. angles (also the number of sides) for the polygon to have.
  1069. Angle! specifies the rotation of the polygon, and is specified
  1070. in radians.
  1071.  
  1072. When it comes to 256-color graphics modes, PowerBasic provides
  1073. no way to get or set the palette. The 256-color modes allow you
  1074. to individually select the actual color to assign to a given
  1075. color number. The modes are said to have 256 colors because you
  1076. may use up to 256 at a time (numbered 0 to 255). You may choose
  1077. the color for a given color number from a much larger palette--
  1078. a bit under 262,144 colors, in fact. Color 0 is black by
  1079. default, for example, but there's no reason you can't redefine
  1080. it to mean pale turquoise. In order to do this, you must choose
  1081. the proper color by blending three available colors-- red,
  1082. green, and blue-- in varying amounts. Each may have an intensity
  1083. from 0 (off) to 63 (bright). If you set both RED and BLUE to 32
  1084. and GREEN to 0, for example, you'd get a medium purple color. It
  1085. may take some experimentation to get just the color you want.
  1086. Note that the intensities are finely graded: you are unlikely to
  1087. see any difference between an intensity of 0 and an intensity of
  1088. 3 in a color, for example.
  1089.  
  1090.                        Graphics Support                  page 24
  1091.  
  1092.  
  1093.  
  1094. The basic routines for getting and setting palette colors are as
  1095. follows. Remember that the color number may be 0-255, while each
  1096. of the intensities can be 0-63.
  1097.  
  1098.    GetPalRGB ColorNr%, RedValue%, GreenValue%, BlueValue%
  1099.    SetPalRGB ColorNr%, RedValue%, GreenValue%, BlueValue%
  1100.  
  1101. If you're not concerned with the actual intensity levels, you
  1102. may find it more convenient to treat the color value as a unit,
  1103. stored in a long integer. You can do that directly:
  1104.  
  1105.    ColorValue& = GetPal& (ColorNr%)
  1106.    SetPal ColorNr%, ColorValue%
  1107.  
  1108. It's also easy enough to convert between the formats:
  1109.  
  1110.    ColorValue& = JoinRGB& (RedValue%, GreenValue%, BlueValue%)
  1111.    SplitRGB ColorValue&, RedValue%, GreenValue%, BlueValue%
  1112.  
  1113. The most obvious aspect of palettes is in the ability to select
  1114. a set of colors suited to a specific application. When showing a
  1115. picture of the sea, you might want mostly blues and greens, for
  1116. instance. There are other implications in the ability to quickly
  1117. change a color across the entire screen, however. It allows for
  1118. simple animation, the ability to fade in or fade out, and other
  1119. interesting effects. Let your imagination run loose and
  1120. experiment a little! You'll be surprised by the power of palette
  1121. manipulation.
  1122.  
  1123.                            Joystick                      page 25
  1124.  
  1125.  
  1126.  
  1127. There's little enough to say about the joystick. A PC may have
  1128. up to two of them. Normally, a joystick has two buttons, and
  1129. returns a pair of coordinates which describe the position in
  1130. which it is being held. The coordinates vary depending on the
  1131. individual joystick, the computer involved, and other factors,
  1132. so it is wise to provide a calibration routine to customize your
  1133. program for the joystick(s) involved.
  1134.  
  1135. The FlightStick joystick has a dial for throttle control, which
  1136. is available only in a one-joystick setup. The throttle value is
  1137. returned as the Y coordinate of an imaginary second joystick.
  1138.  
  1139. Coordinates (X, Y), where X is the horizontal and Y the vertical
  1140. coordinate, range from around 0 to around 150 on my FlightStick.
  1141. Since these values may vary significantly, however, they are
  1142. returned as words.
  1143.  
  1144. The state of the joystick buttons may be checked individually or
  1145. all at once. If you need to know the state of multiple buttons,
  1146. it is faster to do it all at once, but you may do it either way.
  1147.  
  1148.    A1% = JButtonA1%     ' button 1 on 1st joystick
  1149.    A2% = JButtonA2%     ' button 2 on 1st joystick
  1150.    B1% = JButtonB1%     ' button 1 on 2nd joystick
  1151.    B2% = JButtonB2%     ' button 2 on 2nd joystick
  1152.  
  1153.    JButtons A1%, A2%, B1%, B2%    ' all buttons
  1154.  
  1155. The joystick positions are handled internally by a rather
  1156. unfortunate method involving a timer, due to the design of the
  1157. joystick interface. IBM's early PC strategy was to do everything
  1158. possible in software, which lead to rather inefficient joystick
  1159. and CGA handling. In the case of the joystick, this makes it
  1160. important to get all coordinates at once.
  1161.  
  1162.    JPos AX%, AY%, BX%, BY%        ' all joystick positions
  1163.  
  1164. Joystick support is handled through a set of BIOS routines which
  1165. were added to the PC around 1985. These routines are not present
  1166. in some of the oldest PCs.
  1167.  
  1168.                        Keyboard Control                  page 26
  1169.  
  1170.  
  1171.  
  1172. The keyboard is not a particularly exciting or glamorous device.
  1173. In fact, we tend to forget about it except when it gets in the
  1174. way. Sometimes it's a hardware problem-- squishy or clacking
  1175. keys, or perhaps a commonly-used key placed in an out-of-the-way
  1176. location. Then again, sometimes it's the software that's the
  1177. problem. There are many aspects of keyboard control, not all of
  1178. which are necessarily related to input. This unit will let you
  1179. handle the keyboard in ways you may not have realized were
  1180. possible. Better yet, it can help make keyboard control easier
  1181. than the users of your programs dreamed possible.
  1182.  
  1183. Let's start out with keyboard output. Yep, not input-- output.
  1184. You can stuff up to 15 keys into the keyboard buffer. Why would
  1185. you want to do this? Perhaps to allow your program to pop-up a
  1186. TSR automatically, to start another program after your program
  1187. ends, or for creating key macros. You can enter extended key
  1188. codes (such as function keys) by using CHR$(0) before the scan
  1189. code.
  1190.  
  1191.    TypeIn St$
  1192.  
  1193. The usual keyboard action is somewhat sluggish. We can make it a
  1194. lot crisper by changing the key repeat rate and the delay before
  1195. repeating begins. This will work on ATs, but not PC/XT systems.
  1196.  
  1197.    SpeedKey RepDelay%, RepRate%
  1198.  
  1199. The delay may be 0-3 (1 by default):
  1200.  
  1201.    0      250 milliseconds
  1202.    1      500 milliseconds
  1203.    2      750 milliseconds
  1204.    3        1 second
  1205.  
  1206. The repeat rate may be 0-31 (11 by default). The larger the
  1207. number, the slower the speed-- 0 is around 30 cps, and 31 is
  1208. around 2 cps.
  1209.  
  1210. I generally prefer to have the keyboard cranked up to full
  1211. speed, using RepDelay% and RepRate% both set to zero. This may
  1212. be a bit too zippy for some people. Experiment with it to see
  1213. what you like best.
  1214.  
  1215. Of course, there may be reasons to make keyboard repeat less
  1216. sensitive instead! That might be a good idea in programs written
  1217. for small children, for example. You can adjust the keyboard
  1218. equally well in either direction.
  1219.  
  1220.                        Keyboard Control                  page 27
  1221.  
  1222.  
  1223.  
  1224. PowerBasic allows you to control one of the keys which can
  1225. interrupt your program, namely the Break key. There's another
  1226. dangerous key which PBWiz allows you to control-- the PrtSc
  1227. (PrintScreen) key. PrtSc may not seem like a hazard at first
  1228. glance, but if it's pressed by accident with no printer ready,
  1229. or in a graphics mode which PrtSc doesn't understand how to
  1230. print, the results can be pretty messy. So, we let you turn it
  1231. off or back on:
  1232.  
  1233.    SetPrtSc PrtScON%
  1234.  
  1235. Use 0 to turn it off or anything else to turn it back on. If you
  1236. turn off PrtSc, you MUST remember to turn it back on again
  1237. before your program ends! Otherwise, an interrupt vector will
  1238. point into nowhere, causing probable chaos the next time PrtSc
  1239. is pressed.
  1240.  
  1241. Regardless of whether you've turned the PrtSc key off, you can
  1242. print the screen yourself just as if PrtSc had been pressed:
  1243.  
  1244.    PrintScreen
  1245.  
  1246. Now here's a strange one for you. When IBM brought out the
  1247. 101-key keyboard, called the "enhanced" keyboard, they did
  1248. something bizarre to the BIOS. They still allowed old keyboard
  1249. calls to work, but they filtered out the new key codes so no one
  1250. would see them. This made sure that no one would be able to use
  1251. the capabilities of the "enhanced" keyboard without rewriting
  1252. their programs. So, the keyboard has been around for years, and
  1253. there are still few programs that even notice when you press
  1254. F11. PowerBasic v2.1 does not support the enhanced keyboard at
  1255. all. Fortunately, PBWiz -does-. You can find out if an enhanced
  1256. keyboard is installed with the KbdType% function, which is in
  1257. the Equipment unit.
  1258.  
  1259. If there is an enhanced keyboard installed, you can activate it
  1260. like so:
  1261.  
  1262.    SetEnhKbd Enhanced%
  1263.  
  1264. With enhanced keyboard support activated, all key requests that
  1265. used the old services are translated to the new services. So,
  1266. SetEnhKbd affects INKEY$ and other BASIC functions as well as
  1267. other PBWiz keyboard routines. Note that you MUST deactivate
  1268. enhanced keyboard support before ending your program. Otherwise,
  1269. an interrupt vector will point into nowhere, probably causing a
  1270. crash on the next keypress!
  1271.  
  1272.                        Keyboard Control                  page 28
  1273.  
  1274.  
  1275.  
  1276. Speaking of INKEY$, I have a neat little function for you. It
  1277. works like INKEY$, but it doesn't remove the key from the
  1278. keyboard buffer:
  1279.  
  1280.    ky$ = ScanKey$
  1281.  
  1282. How is this handy? Well, let's suppose you're writing something
  1283. that will work like the DIR or TYPE commands in DOS. It will
  1284. display what may be a long listing, which you'd like to be able
  1285. to pause with Ctrl-S or cancel with Ctrl-C. Trouble is, if you
  1286. use INKEY$ and the user was using "type ahead" to store another
  1287. command, you've just wiped out his command while looking for
  1288. those control codes. With ScanKey$, you can check the key
  1289. nondestructively.
  1290.  
  1291. On the other hand, if you're about to request important input,
  1292. you may not want to chance having it answered from results of
  1293. the keyboard buffer-- could be that the user meant those keys
  1294. for another purpose. In that case, it's a good approach to clear
  1295. out the keyboard buffer just before the input:
  1296.  
  1297.    ClearKbd
  1298.  
  1299. No keyboard unit would be complete without a selection of
  1300. routines to check the shift states and get or set the keyboard
  1301. toggles. Let's start with the toggles, which are so called
  1302. because they get toggled from one state to another:
  1303.  
  1304.     PRINT "Caps Lock  : ";
  1305.     IF CapsOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1306.     PRINT "Insert     : ";
  1307.     IF InsertOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1308.     PRINT "Num Lock   : ";
  1309.     IF NumOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1310.     PRINT "Scroll Lock: ";
  1311.     IF ScrollOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1312.  
  1313. You can also turn the toggles off or on. It's courteous to
  1314. restore the original toggle states once you end your program, so
  1315. you might want to save the original values for that purpose.
  1316. Then again, I guess that doesn't apply if your program is
  1317. designed for the specific purpose of setting the toggles!
  1318.  
  1319.    SetCaps CapsLock%
  1320.    SetInsert InsertState%
  1321.    SetNum NumLock%
  1322.    SetScroll ScrollLock%
  1323.  
  1324. Does anyone actually use ScrollLock for anything? Just
  1325. wondering...
  1326.  
  1327.                        Keyboard Control                  page 29
  1328.  
  1329.  
  1330.  
  1331. The shift keys are unique in many respects. They don't return
  1332. codes that can be detected with INKEY$ or stuffed into the
  1333. keyboard buffer; several can be pressed at the same time; and
  1334. they don't repeat. You can detect 'em with PBWiz, at any rate:
  1335.  
  1336.    IF AltPress% THEN PRINT "An ALT key is pressed."
  1337.    IF CtrlPress% THEN PRINT "A CTRL key is pressed."
  1338.    IF ShiftPress% THEN PRINT "A SHIFT key is pressed."
  1339.  
  1340.    IF LAltPress% THEN PRINT "The LEFT ALT key is pressed."
  1341.    IF LCtrlPress% THEN PRINT "The LEFT CTRL key is pressed."
  1342.    IF LShiftPress% THEN PRINT "The LEFT SHIFT key is pressed."
  1343.  
  1344.    IF RAltPress% THEN PRINT "The RIGHT ALT key is pressed."
  1345.    IF RCtrlPress% THEN PRINT "The RIGHT CTRL key is pressed."
  1346.    IF RShiftPress% THEN PRINT "The RIGHT SHIFT key is pressed."
  1347.  
  1348. NOTE that LAltPress%, LCtrlPress%, RAltPress%, and RCtrlPress%
  1349. are ONLY available for enhanced keyboards. They will not return
  1350. useful results on older keyboards.
  1351.  
  1352.                          Memory (EMS)                    page 30
  1353.  
  1354.  
  1355.  
  1356. This unit provides support for expanded memory. It will work
  1357. with older EMS and EEMS drivers as well as the current EMS 4.0
  1358. standard, with the exception of one or two routines (as noted)
  1359. which take advantage of new capabilities.
  1360.  
  1361. Expanded memory may be present on any type of computer, from
  1362. 8088 PCs to 80486 ATs. It usually comes as a hardware board with
  1363. a software driver for older machines; on ATs, it may be
  1364. implemented using only a software driver which converts it from
  1365. extended memory. Drivers have also been written which make a
  1366. hard disk function as EMS memory. This broad range of use makes
  1367. EMS support invaluable to programs which need extra memory. EMS
  1368. can theoretically support up to 1 gigabyte of RAM, although the
  1369. documented limit as of v4.0 was only 32 megabytes.
  1370.  
  1371. Of course, the first thing you need to know is when dealing with
  1372. EMS is whether any EMS memory actually exists:
  1373.  
  1374.    IF EmsExists% THEN PRINT "EMS exists"
  1375.  
  1376. The EMS version may also be retrieved:
  1377.  
  1378.    EmsVer MajorV%, MinorV%
  1379.  
  1380. It would be a good idea to check the EMS version if you plan to
  1381. use any features which are only available as of EMS 4.0, such as
  1382. reallocation.
  1383.  
  1384. Besides mere existence and version checks, you will want to know
  1385. how much EMS is available:
  1386.  
  1387.    PRINT "Total EMS installed: "; EmsTotal%
  1388.    PRINT "Free EMS memory    : "; EmsFree%
  1389.  
  1390. If you actually tried the above two lines, you would get a pair
  1391. of values which don't seem to mean much. The trick is to
  1392. multiply them by 16,384 to convert them to bytes. This is
  1393. because EMS memory is always accessed in pages of 16k bytes
  1394. each. Any time you are dealing with a quantity of EMS memory,
  1395. the quantity will be specified as a number of pages.
  1396.  
  1397.                          Memory (EMS)                    page 31
  1398.  
  1399.  
  1400.  
  1401. Before we get into the mechanics of accessing EMS memory, I'd
  1402. like to bring up an optional routine which can improve access
  1403. speed. It should not be used if your program accesses EMS using
  1404. routines other than the ones included here in PBWiz. If you only
  1405. use these EMS routines, though, you will find that it makes some
  1406. kinds of memory accesses faster. Use 0 for normal (slow) mode.
  1407. Do not use optimization if you are using more than one EMS
  1408. handle!
  1409.  
  1410.    EmsOpt Fast%
  1411.  
  1412. Ok, let's get down to the nitty gritty. (Where did that
  1413. expression come from, anyway?!) When you allocate EMS memory,
  1414. you specify the number of pages you want, which must be at least
  1415. 1. If the allocation is successful, you are returned a "handle"
  1416. which you can use to access the allocated memory. Otherwise, you
  1417. get back an error code.
  1418.  
  1419.    EmsOpen Pages%, Handle%, ErrCode%
  1420.  
  1421. There are a limited number of handles available under some EMS
  1422. drivers. The EMS spec, as of v4.0, allowed for a maximum of 255
  1423. handles, and it's not unusual for a driver to support only 20 or
  1424. so. Bearing in mind that some of these handles may be used up by
  1425. other applications, such as RAMdisks and caches, this really
  1426. doesn't allow much leeway. Try to use as few handles as
  1427. possible! You may well need to store multiple values in
  1428. different areas of the memory allocated for a single handle,
  1429. rather than allocating a new area of memory for each value.
  1430.  
  1431. Suppose you find you need more memory than you first allocated?
  1432. Or maybe less memory? Well, provided EMS 4.0 or later is in use,
  1433. you can reallocate the block:
  1434.  
  1435.    EmsResize Handle%, Pages%, ErrCode%
  1436.  
  1437. There is no way to reallocate memory under older versions of
  1438. EMS. About the best you could do in that case would be to
  1439. allocate a new area of the desired size, copy over the relevant
  1440. data from the old area, and then deallocate the original area.
  1441. Of course, this assumes that there is enough memory available to
  1442. hold both areas, at least temporarily.
  1443.  
  1444. When you are finished using EMS, you must be sure to return the
  1445. memory you allocated to the system. It is IMPORTANT to do this
  1446. before ending your program. Otherwise, the memory you allocated
  1447. will be "lost" until the next time you boot the computer. Return
  1448. the memory for each handle as follows:
  1449.  
  1450.    EmsClose Handle%
  1451.  
  1452.                          Memory (EMS)                    page 32
  1453.  
  1454.  
  1455.  
  1456. Hmmmm... we've covered EMS detection, status info, allocating
  1457. memory, freeing memory, resizing memory... what's missing here?
  1458. Ah! We haven't discussed how to actually access the memory!
  1459.  
  1460. Accessing EMS memory is quite simple, but it involves a couple
  1461. of steps. EMS is mapped into a 64k block in the low area of
  1462. memory (the area under 1M, which can be directly accessed).
  1463. Since a page is 16k, the EMS block can hold up to four pages at
  1464. a time. To transfer data between normal memory and EMS memory,
  1465. you must map the appropriate page(s) of EMS into the EMS block.
  1466. This is done like so:
  1467.  
  1468.     EmsMap Handle%, PPage%, VPage%
  1469.  
  1470. The PPage% is the physical page, that is, the page number within
  1471. the EMS block (0-3). The VPage% is the virtual page, which is
  1472. the number of a page within the EMS memory associated with
  1473. Handle%.
  1474.  
  1475. Once you've mapped the desired virtual page into a physical
  1476. page, it can be accessed using standard PowerBasic memory
  1477. commands, such PEEK$ and POKE$. First, set the segment:
  1478.  
  1479.    DEF SEG=EmsSeg&
  1480.  
  1481. The offset within a page may be 0-16,383. To get the appropriate
  1482. offset within the EMS block, you must add the offset of the page
  1483. itself. This may be calculated as follows:
  1484.  
  1485.    DataOffset& = OffsetWithinPage% + PageNumber% * 16384&
  1486.  
  1487. One final note: for best compatibility, it would be good to
  1488. avoid using physical page 3 (the last 16k of the EMS block).
  1489. Some EMS drivers don't handle this page with complete accuracy,
  1490. for technical reasons I'm not going to get into right now.
  1491.  
  1492.                          Memory (XMS)                    page 33
  1493.  
  1494.  
  1495.  
  1496. This unit provides support for XMS extended memory. It won't
  1497. work with extended memory unless an XMS driver is present.
  1498.  
  1499. Extended memory is only present in AT-class computers. It is not
  1500. available on older PCs. An XMS driver must also be used. XMS
  1501. drivers are included with MS-DOS 5.0 and Windows 3.0, among
  1502. other things, so this is not a major limitation. XMS can address
  1503. a maximum of 64 megabytes.
  1504.  
  1505. The first thing to check is whether any XMS memory exists:
  1506.  
  1507.    IF XmsExists% THEN PRINT "XMS exists"
  1508.  
  1509. The Xms version may also be retrieved:
  1510.  
  1511.    XmsVer MajorV%, MinorV%
  1512.  
  1513. The amount of XMS memory available may be reported in either of
  1514. two different ways: total amount and largest available block.
  1515.  
  1516.    PRINT "Total XMS free    : "; XmsTfree&
  1517.    PRINT "Largest free block: "; XmsLfree&
  1518.  
  1519. XMS memory is manipulated in terms of 1,024 byte blocks, so the
  1520. amount of free memory is reported in kilobytes. Any time you are
  1521. dealing with a quantity of EMS memory, the quantity will be
  1522. specified as a number of 1K blocks, except as otherwise noted.
  1523.  
  1524.                          Memory (XMS)                    page 34
  1525.  
  1526.  
  1527.  
  1528. When you allocate XMS memory, you specify the number of
  1529. kilobytes that you want. This may be 0-65535, in theory. Dunno,
  1530. I don't have 64M RAM <grin>. If the allocation is successful,
  1531. you are returned a "handle" which you can use to access the
  1532. allocated memory. Otherwise, you get back an error code.
  1533.  
  1534.    XmsOpen KBytes&, Handle%, ErrCode%
  1535.  
  1536. There are a limited number of handles available, although the
  1537. number can be controlled somewhat by a driver parameter. It's
  1538. probably best to use as few handles as possible, to avoid
  1539. running out. You may well want to store multiple values in
  1540. different areas of the memory allocated for a single handle,
  1541. rather than allocating a new area of memory for each value.
  1542.  
  1543. Suppose you find you need more memory than you first allocated?
  1544. Or maybe less memory? Just reallocate the block:
  1545.  
  1546.    XmsResize Handle%, KBytes&, ErrCode%
  1547.  
  1548. When you are finished using XMS, you must be sure to return the
  1549. memory you allocated to the system. It is IMPORTANT to do this
  1550. before ending your program. Otherwise, the memory you allocated
  1551. will be "lost" until the next time you boot the computer. Return
  1552. the memory for each handle as follows:
  1553.  
  1554.    XmsClose Handle%
  1555.  
  1556. To transfer data between normal memory and XMS memory, you must
  1557. provide the segment and offset of the normal memory area (use
  1558. the PowerBasic functions VARSEG and VARPTR to find the address
  1559. of a variable). The position within XMS memory is specified as a
  1560. long-integer offset starting at zero.
  1561.  
  1562.     XmsRead Handle%, Posn&, Bytes&, DSeg%, DOfs%
  1563.  
  1564.     XmsWrite Handle%, Posn&, Bytes&, DSeg%, DOfs%
  1565.  
  1566. Note that the Bytes& to transfer must be an EVEN NUMBER. It is
  1567. not restricted to 64k, however, so you can transfer a great deal
  1568. of data with these routines.
  1569.  
  1570. The XMS spec guarantees a "reasonable" number of interrupt
  1571. windows during a transfer; however, it is possible that you
  1572. might experience some communications dropouts if you do large
  1573. transfers during high-speed telecommunications. If you are
  1574. making heavy demands on the interrupt system, be sure to test
  1575. your program carefully under maximum load conditions.
  1576.  
  1577. A brief demo program is included which shows how to use XMS for
  1578. holding arrays. The demo, XMSDEMO.BAS, shows a two-dimensional
  1579. long integer array. This can be readily altered to support any
  1580. data type composed of an even number of bytes.
  1581.  
  1582.                          Mouse Support                   page 35
  1583.  
  1584.  
  1585.  
  1586. The mouse unit provides full-featured mouse support. You can see
  1587. if a mouse is available and how many buttons it has, get the
  1588. cursor position (either the current position or the position at
  1589. the last press or release of a specified button), set the cursor
  1590. position, change the cursor, set the mouse range, get hardware
  1591. information about the mouse, and so on.
  1592.  
  1593. There are two unusual mouse modes to be aware of. One is text
  1594. mode, which is mapped to a 640x200 virtual display. So, to
  1595. convert the results to text format, you need to divide the
  1596. cursor position by eight and add one. To convert from text
  1597. format, subtract one and multiply by eight.
  1598.  
  1599. The second unusual mode is 320x200 CGA mode, which is also
  1600. mapped to 640x200. To convert the coordinates to this mode,
  1601. divide X by two. To convert from this mode, multiply the X
  1602. coordinate by two.
  1603.  
  1604. All other modes use the actual display coordinates instead of a
  1605. bizarro virtual screen. Why the peculiar CGA and text modes?
  1606. Well, evidently Microsoft never thought there'd be any video
  1607. adapters besides MDA and CGA, and decided to create a single
  1608. virtual screen size that worked for all modes. Not a bad idea, I
  1609. guess, but rather shortsighted. Oh well.
  1610.  
  1611. One other nuisance that you may run into is that the mouse
  1612. cursor can't be directly turned on or off. A "cursor visibility"
  1613. count is maintained-- if the mouse cursor was turned on twice,
  1614. you'll need to turn it off twice before it will actually
  1615. disappear.
  1616.  
  1617. Before using the mouse, you must initialize it. The
  1618. initialization routine also checks to make sure that a mouse is
  1619. installed and tells you how many buttons it has. It's best to
  1620. initialize the mouse after setting the screen mode, so the mouse
  1621. driver understands what mode you're using. Not all mouse drivers
  1622. support all screen modes, but you can reasonably expect any
  1623. current mouse driver to support MDA, CGA, EGA, and VGA. Hercules
  1624. graphics mode is rarely supported, as it must be set through
  1625. direct hardware access rather than the standard techniques, so
  1626. the mouse driver has little way of knowing that you've changed
  1627. the mode.
  1628.  
  1629. The mouse routines will work equally well with two-button or
  1630. three-button rodents. The middle button functions will return 0
  1631. with two-button mice.
  1632.  
  1633.                          Mouse Support                   page 36
  1634.  
  1635.  
  1636.  
  1637. I won't go into great detail on these routines, because they're
  1638. pretty much self-explanatory. The mouse is a fairly easy device
  1639. to deal with.
  1640.  
  1641. You can initialize the mouse driver like so:
  1642.  
  1643.    Buttons% = MouseInit%
  1644.  
  1645. This returns the number of mouse buttons available. If there is
  1646. no mouse, zero will be returned. Initialize the mouse after
  1647. setting the screen mode.
  1648.  
  1649. You can make the mouse cursor visible or invisible. It will
  1650. function just as well in either state. See the previous page for
  1651. some quirks.
  1652.  
  1653.    MouseShow       ' show the cursor
  1654.    MouseHide       ' hide the cursor
  1655.  
  1656. There are many ways to get the mouse cursor position. You can
  1657. get the current position, the position at which the mouse was
  1658. located when a particular button was pressed, or the position
  1659. when a button was released. If you choose a past position, you
  1660. can also find out how many presses or releases of the button
  1661. have taken place since you last checked.
  1662.  
  1663.    X% = MouseWhereX%              ' current X coordinate
  1664.    Y% = MouseWhereY%              ' current Y coordinate
  1665.  
  1666.    MouseLClick Count%, X%, Y%     ' left presses & posn
  1667.    MouseMClick Count%, X%, Y%     ' middle presses & posn
  1668.    MouseRClick Count%, X%, Y%     ' right presses & posn
  1669.  
  1670.    MouseLRelease Count%, X%, Y%   ' left releases & posn
  1671.    MouseMRelease Count%, X%, Y%   ' middle releases & posn
  1672.    MouseRRelease Count%, X%, Y%   ' right releases & posn
  1673.  
  1674. If you'd prefer to find out which buttons are currently pressed,
  1675. no problem:
  1676.  
  1677.    Pressed% = MouseLButton%  ' whether left button is pressed
  1678.    Pressed% = MouseMButton%  ' whether middle button is pressed
  1679.    Pressed% = MouseRButton%  ' whether right button is pressed
  1680.  
  1681. Of course, you can also set the cursor location:
  1682.  
  1683.    MouseLocate X%, Y%        ' set the mouse cursor position
  1684.  
  1685. The mouse cursor range can be restricted to a given area of the
  1686. screen. This area is expressed by giving the upper left corner
  1687. and lower right corner of the rectangular area to which to
  1688. restrict the cursor.
  1689.  
  1690.    MouseWindow X1%, Y1%, X2%, Y2%      ' set mouse cursor range
  1691.  
  1692.                          Mouse Support                   page 37
  1693.  
  1694.  
  1695.  
  1696. There are a variety of cursor shapes available for graphics
  1697. mode:
  1698.  
  1699.     0    hourglass ("please wait, program is working" symbol)
  1700.     1    pointing arrow (default)
  1701.     2    pointing hand
  1702.     3    crosshair
  1703.     4    target (box in a box)
  1704.     5    grabbing hand
  1705.  
  1706. If you have ideas for more, let me know and I'll see what I can
  1707. do. Cursor shapes are not unduly difficult to define.
  1708.  
  1709. The cursor image is set like so:
  1710.  
  1711.    MouseCursorG CursorNr%
  1712.  
  1713.                           SoundBlaster                   page 38
  1714.  
  1715.  
  1716.  
  1717. The SoundBlaster is one of the most popular sound boards around
  1718. these days, despite an early hardware design which conflicts
  1719. with standard parallel ports and a nearly complete absence of
  1720. programming tools. Go figure.
  1721.  
  1722. Anyway, in this here unit, we provide a range of support for the
  1723. SoundBlaster SBSIM driver, and any other sound boards which
  1724. provide drivers with the same functionality. You must load the
  1725. SBSIM driver to use these routines-- just scoot over to your
  1726. SoundBlaster directory and run SBSIM. It will probably give you
  1727. a couple of error messages, which can be safely ignored. They
  1728. refer to optional SoundBlaster capabilities which are probably
  1729. not on your machine. You can test for successful installation
  1730. with the routines in this unit.
  1731.  
  1732. The first thing you must do is check whether the SBSIM driver is
  1733. installed. This is done by checking the software interrupt used
  1734. by the driver. If the number returned is zero, SBSIM is not
  1735. installed. You probably won't need to know the actual interrupt
  1736. number itself, but it's returned in case you want to use SBSIM
  1737. in ways that aren't supported by PBWiz.
  1738.  
  1739.    IntNr% = SBInt%
  1740.  
  1741. SBSIM is actually a super-driver. It loads a number of other
  1742. drivers, depending on the configuration of the sound board and
  1743. the SBSIM.CFG file. Possible sub-drivers include an FM driver
  1744. (.CMF files), disk and memory voice drivers (.VOC files), a MIDI
  1745. driver (.MID files), and an auxiliary driver. You can check
  1746. which drivers are available like so:
  1747.  
  1748.    SBGetDrivers FM%, DskVoice%, MemVoice%, Auxiliary%, MIDI%
  1749.  
  1750. The difference between the disk and memory voice drivers is that
  1751. the disk voice driver can play a .VOC file directly, whereas the
  1752. memory voice driver plays .VOC files that have been loaded into
  1753. XMS memory. The auxiliary driver is not supported by the current
  1754. version of PBWiz. The routine returns 0 if the driver is not
  1755. installed, or -1 if it is.
  1756.  
  1757. A similar routine can be used to detect which drivers are in use
  1758. at the moment:
  1759.  
  1760.    SBGetActive FM%, DskVoice%, MemVoice%, Auxiliary%, MIDI%
  1761.  
  1762. The version of the SBSIM driver can be retrieved as well:
  1763.  
  1764.    SBGetVer MajorV%, MinorV%
  1765.  
  1766. Given a version of 1.20, this would return MajorV% = 1 and
  1767. MinorV% = 20. The minor version number is generally not of much
  1768. importance and is mostly useful for informational purposes.
  1769.  
  1770.                           SoundBlaster                   page 39
  1771.  
  1772.  
  1773.  
  1774. The volume level for various SBSIM devices can be retrieved or
  1775. set for both stereo and mono SoundBlasters. Only the "left"
  1776. speaker is active in the case of mono SoundBlasters. The volume
  1777. level appears to range from 0 (off) to 255 (full).
  1778.  
  1779.    SBGetVolume Source%, LeftVol%, RightVol%
  1780.    SBSetVolume Source%, LeftVol%, RightVol%
  1781.  
  1782. Sources may be any of the following:
  1783.  
  1784.    0    master volume         4    line in
  1785.    1    voice                 5    microphone
  1786.    2    FM                    6    PC speaker
  1787.    3    CD player
  1788.  
  1789. There are several ways to set up the MIDI channel mapper. This
  1790. is done like so:
  1791.  
  1792.    SBMapMIDI MapNr%
  1793.  
  1794. You may choose from the following mappers:
  1795.  
  1796.    0    general mapper
  1797.    1    basic mapper
  1798.    2    extended mapper
  1799.  
  1800. That pretty well covers the generic status routines. The rest of
  1801. the SoundBlaster routines are concerned with actually playing
  1802. sounds, and related functions such as pause and resume.
  1803.  
  1804. Before playing a sound, you need to initialize the sound driver.
  1805. You may choose to play a sound file directly from disk (in the
  1806. case of .CMF, .VOC, and .MID files) or from memory (.VOC only).
  1807. Although all sounds are played in the background and do not
  1808. interfere with your program operation, you will get smoother
  1809. response by loading .VOC files into XMS memory beforehand, if
  1810. plenty of memory is available.
  1811.  
  1812. You initialize a file to play directly from disk like so:
  1813.  
  1814.    SBInitSrcFile DriverNr%, FileName$, ErrCode%
  1815.  
  1816. The file name must include the extension. You may choose from
  1817. the following drivers:
  1818.  
  1819.    1    .CMF    FM synthesis
  1820.    2    .VOC    disk voice
  1821.    5    .MID    MIDI
  1822.  
  1823. Remember, this only initializes the file to play. It does not
  1824. actually start playing the sound. We'll get to that shortly.
  1825.  
  1826.                           SoundBlaster                   page 40
  1827.  
  1828.  
  1829.  
  1830. Files can also be loaded into XMS memory before playing, in the
  1831. case of .VOC files only. A handle is returned which can later be
  1832. used to identify the sound to play. Normally, you should set the
  1833. handle to zero before calling the routine, which forces the
  1834. routine to use the first available handle it can find. The real
  1835. handle is returned to you.
  1836.  
  1837.    SBLoadXms FileName$, Handle%, ErrCode%
  1838.  
  1839. In the case of sounds loaded into memory, the initialization is
  1840. done as follows:
  1841.  
  1842.    SBInitSrcXms Handle%, ErrCode%
  1843.  
  1844. When you have finished using a handle, be sure to free it, or
  1845. the associated memory will be allocated until the computer is
  1846. rebooted. Freeing a handle is easy:
  1847.  
  1848.    SBFreeXms Handle%
  1849.  
  1850. You can check for the number of free handles as well:
  1851.  
  1852.    Free% = SBFreeHandles%
  1853.  
  1854. And, for that matter, see if a specific handle is free:
  1855.  
  1856.    Free% = SBIsFree%(Handle%)
  1857.  
  1858. Once the sound driver is initialized, you may start it playing,
  1859. pause it, resume, check its status, or stop it:
  1860.  
  1861.    SBPlay Driver%
  1862.    SBPause Driver%
  1863.    SBResume Driver%
  1864.    SBStop Driver%
  1865.    State% = SBStatus% (Driver%)
  1866.  
  1867. The driver may be any of the following:
  1868.  
  1869.    1   .CMF    FM synthesis
  1870.    2   .VOC    disk voice
  1871.    3   (XMS)   memory voice
  1872.    5   .MID    MIDI
  1873.  
  1874. Note that the status value is often -1 while a driver is playing
  1875. and 0 while it isn't, but different status values may be
  1876. embedded in the sound file. If your aim is to determine whether
  1877. a sound is still playing, use SBGetActive instead of SBStatus%.
  1878.  
  1879. See the PLAYVOC.BAS file for a demo of how these routines work
  1880. together in practice.
  1881.  
  1882.                             Strings                      page 41
  1883.  
  1884.  
  1885.  
  1886. One of the true strengths of BASIC in general, and PowerBasic in
  1887. particular, is its powerful string handling capability. I'd be
  1888. remiss if I didn't provide extensions which improve it even
  1889. further.
  1890.  
  1891. The simplest of the PBWiz string routines may seem somewhat
  1892. whimsical, but it has proven useful to me on several occasions
  1893. in the past. It reverses the order of characters in a string.
  1894.  
  1895.    Reverse St$
  1896.  
  1897. One of the places this has come in useful is in searching a
  1898. string from the end-- a reverse INSTR routine:
  1899.  
  1900.    RInstr MainSt$, SubSt$, Posn%
  1901.  
  1902. Rather than returning the first occurrence of a substring within
  1903. a main string, it returns the last occurrence. Another handy
  1904. string search allows you to search for various types of
  1905. characters, rather than a specific substring:
  1906.  
  1907.    TInstr MainSt$, Types%, Posn%
  1908.  
  1909. The type(s) may be specified using any combination of the
  1910. following. Just add them together.
  1911.  
  1912.     1    alphabetic
  1913.     2    numeric
  1914.     4    symbolic
  1915.     8    control
  1916.    16    graphics
  1917.    32    space
  1918.  
  1919. Since you can search for any specific types, you can also
  1920. readily invert the search to look for any characters that are
  1921. NOT of a given type or types:
  1922.  
  1923.    Types% = NOT Types%
  1924.  
  1925. This gives you complete control. A typical use for TInstr might
  1926. be to clean up user input and make sure that it's valid. It's
  1927. also good for parsing.
  1928.  
  1929.                             Strings                      page 42
  1930.  
  1931.  
  1932.  
  1933. Another routine that is useful for cleaning up and parsing user
  1934. input is called Crunch. It allows you to eliminate adjacent
  1935. duplicates of a character or list of characters. One use for
  1936. this, for instance, would be to eliminate repeated spaces,
  1937. converting an input string from "*.*    *.BAK  /B" to a more
  1938. manageable "*.* *.BAK /B".
  1939.  
  1940.    Result$ = Crunch$(St$, CharList$)
  1941.  
  1942. There are a pair of routines that you'll find valuable if you
  1943. need to check the validity of a string. These are designed to be
  1944. compatible with the Xmodem and Ymodem file transfer protocols,
  1945. so you can use them for error checking in telecommunications as
  1946. well.
  1947.  
  1948.    Chk% = CheckSum% (St$)
  1949.  
  1950.    CRC16 St$, HiCRC%, LoCRC%
  1951.  
  1952. Another pair of string routines provide a simple encryption and
  1953. decryption system for text. The method used is not particularly
  1954. secure but are very fast and will be adequate for many purposes.
  1955. As always, it helps to use a long and/or complex password.
  1956.  
  1957.    Cipher St$, Password$          ' cipher (unprintable)
  1958.    CipherP St$, Password$         ' cipher (printable)
  1959.  
  1960. Both of these routines will encipher text on the first
  1961. run-through and decipher on the second, so you can use the same
  1962. routine either to encrypt or decrypt a message. They are
  1963. different in one respect: the encrypted result of Cipher may
  1964. contain control characters, so it can't be used in a plain
  1965. sequential-access file. The CipherP routine does not allow use
  1966. of extended ASCII characters (CHR$(128) - CHR$(255)), as it sets
  1967. the high bit on each character after encrypting it. This causes
  1968. the results of CipherP to be printable (and useful in
  1969. sequential-access files), although they will look very strange.
  1970.  
  1971. The strings are encrypted (or decrypted) in place. This provides
  1972. a certain extra measure of security for encryption-- the
  1973. original plaintext strings are not left floating around in
  1974. memory where someone might see them.
  1975.  
  1976. One function is as much a file manipulation routine as it is a
  1977. string function. It allows you to compare a file name to a file
  1978. pattern (which may contain wildcards) to see if they match. Only
  1979. bare filespecs are supported-- you may not use drive or path
  1980. specifications in the names.
  1981.  
  1982.    IF MatchFile% (Pattern$, Filename$) THEN PRINT Filename$
  1983.  
  1984.                             Strings                      page 43
  1985.  
  1986.  
  1987.  
  1988. The MatchFile function can be used in creating your own
  1989. DOS-style utilities: DIR, COPY, and so forth. Besides the usual
  1990. "accept file if it matches" approach, it can also be used to
  1991. implement the opposite: "exclude file if it matches." This gives
  1992. you more flexibility than DOS itself supplies.
  1993.  
  1994. The PowerBasic compiler provides a very nice function called
  1995. Extract$. This allows you to retrieve a substring running from
  1996. the left side of a main string to a specified character
  1997. delimiter. Not bad, but it might be handy to be able to grab a
  1998. numbered substring from any part of a main string, and to be
  1999. able to use a substring delimiter. For instance, you might load
  2000. a record from a database which contains an address, where each
  2001. line is delimited by a carriage return and linefeed. Rather than
  2002. mucking around with Extract$, which really wasn't designed with
  2003. that sort of thing in mind, you'd do better to use the PBWiz
  2004. function called DelimExtract$:
  2005.  
  2006.    SubSt$ = DelimExtract$(St$, Delimiter$, Index%)
  2007.  
  2008. The index starts at 1 with the first substring. If you choose
  2009. the index of a substring which doesn't actually exist, a null
  2010. string will be returned.
  2011.  
  2012. Much of the time the ASC (and ASCII) functions are used in
  2013. combination with MID$ to get the code for a character in the
  2014. midst of a string. PBWiz provides a convenient combined function
  2015. which does both at once:
  2016.  
  2017.    ch% = AscM%(St$, Posn%)
  2018.  
  2019. If you specify a position that's past the end of the string, a
  2020. -1 will be returned, just as for the ASCII function.
  2021.  
  2022.                      Telecommunications                 page 44
  2023.  
  2024.  
  2025.  
  2026. This unit provides an assortment of low-level, buffered,
  2027. interrupt-driven telecommunications services. It allows
  2028. comprehensive control over communications, includes COM3 and
  2029. COM4, and doesn't require error trapping. It won't fiddle with
  2030. the DTR unless you tell it to do so. The major limitation is
  2031. that you may use only a single comm port at a time.
  2032.  
  2033. Before you can use communications, you must initialize the
  2034. communications handler. If you didn't have PBWiz, you would
  2035. probably use something like:
  2036.  
  2037.    OPEN "COM1:2400,N,8,1,RS,CS,DS" AS #1
  2038.  
  2039. With PBWiz, you do not have to set the speed, parity, and so
  2040. forth. Communications will proceed with whatever the current
  2041. settings are, unless you choose to specify your own settings.
  2042. When you initialize the comm handler, you specify only the port
  2043. number (1-4):
  2044.  
  2045.    TCInit CPort%, ErrCode%
  2046.  
  2047. When you are done with the telecomm routines, you must terminate
  2048. them. In BASIC, this would look something like:
  2049.  
  2050.    CLOSE #1
  2051.  
  2052. With the PBWiz routines, though, you would use this instead:
  2053.  
  2054.    TCDone
  2055.  
  2056. The PBWiz "TCDone" does not drop the DTR, unlike BASIC's
  2057. "CLOSE". This means that the modem will not automatically be
  2058. told to hang up. With PBWiz, you have complete control over the
  2059. DTR with the TCDTR routine. Use a value of zero to drop the DTR
  2060. or nonzero to raise the DTR:
  2061.  
  2062.    TCDTR DTRstate%
  2063.  
  2064. You may set the speed of the comm port to any baud rate from
  2065. 1-65,535. If you will be dealing with comm programs that were
  2066. not written using PBWiz, you may wish to restrict that to the
  2067. more common rates: 300, 1200, 2400, 4800, 9600, 19200, 38400,
  2068. and 57600.
  2069.  
  2070.    TCSpeed Baud&
  2071.  
  2072.                      Telecommunications                 page 45
  2073.  
  2074.  
  2075.  
  2076. The parity, word length, and stop bits can also be specified.
  2077. You may use 1-2 stop bits, 6-8 bit words, and parity settings of
  2078. None, Even, Odd, Mark, or Space. Nearly all BBSes use settings
  2079. of None, 8 bit words, and 1 stop bit, although you will
  2080. sometimes see Even, 7 bit words, and 1 stop bit. The other
  2081. capabilities are provided for dealing with mainframes and other
  2082. systems which may require unusual communications parameters.
  2083.  
  2084. When specifying parity, only the first character in the string
  2085. is used, and uppercase/lowercase distinctions are ignored. Thus,
  2086. using either "none" or "N" would specify that no parity is to be
  2087. used.
  2088.  
  2089.    TCParms Parity$, WordLength%, StopBits%
  2090.  
  2091. If your program needs to be aware of when a carrier is present,
  2092. it can check the carrier detect signal from the modem with the
  2093. TCCarrier function. This function returns zero if no carrier is
  2094. present:
  2095.  
  2096.    IF TCCarrier% THEN
  2097.       PRINT "Carrier detected"
  2098.    ELSE
  2099.       PRINT "No carrier"
  2100.    END IF
  2101.  
  2102. Suppose, though, that you need to know immediately when someone
  2103. has dropped the carrier? It wouldn't be too convenient to have
  2104. to spot TCCarrier functions all over your program! In that case,
  2105. try the "ON TIMER" facility provided by BASIC for keeping an eye
  2106. on things. It will enable you to check the carrier at specified
  2107. intervals and act accordingly. Here's a brief framework for
  2108. writing such code:
  2109.  
  2110.    ON TIMER(30) GOSUB CarrierCheck
  2111.    TIMER ON
  2112.    ' ...your program goes here...
  2113. CarrierCheck:
  2114.    IF TCCarrier% THEN    ' if the carrier is present...
  2115.       RETURN             ' ...simply resume where we left off
  2116.    ELSE                  ' otherwise...
  2117.       RETURN Restart     ' ...return to the "Restart" label
  2118.    END IF
  2119.  
  2120. To get a character from the comm port, use the TCInkey$
  2121. function. This is available in byte and string flavors:
  2122.  
  2123.    ch$ = TCInkey$
  2124.    ch% = TCInkey0%
  2125.  
  2126.                      Telecommunications                 page 46
  2127.  
  2128.  
  2129.  
  2130. To send a string to the comm port, use TCWrite:
  2131.  
  2132.    TCWrite St$
  2133.  
  2134. If you are dealing strictly with text, you may want to have a
  2135. carriage return and a linefeed added to the end of the string:
  2136.  
  2137.    TCWriteLn St$
  2138.  
  2139. If you'd like to know how many bytes are waiting in the input
  2140. buffer or output buffer, there are functions which will tell
  2141. you:
  2142.  
  2143.    PRINT "Bytes in input buffer:"; TCInStat%
  2144.    PRINT "Bytes in output buffer:"; TCOutStat%
  2145.  
  2146. If you would like to clear the I/O buffers for some reason, you
  2147. can do that too. The following routines clear the buffers,
  2148. discarding anything which was waiting in them:
  2149.  
  2150.    TCFlushIn
  2151.    TCFlushOut
  2152.  
  2153. If you're using a fast modem (9600 bps or greater), you should
  2154. turn on hardware flow control for reliable communications:
  2155.  
  2156.    TCFlowCtl -1
  2157.  
  2158. To get some idea of how these routines all tie together in
  2159. practice, see the MINITERM.BAS example program. It provides a
  2160. tiny "dumb terminal" program to demonstrate the PBWiz comm
  2161. handler. The modem to use is selected via command-line switch:
  2162.  
  2163.    /COM1   use COM1
  2164.    /COM2   use COM2
  2165.    /COM3   use COM3
  2166.    /COM4   use COM4
  2167.  
  2168. By default, the MINITERM.BAS program will use COM1 at 2400 baud
  2169. with no parity, 8 bit words and 1 stop bit. You can exit the
  2170. program by pressing Alt-X.
  2171.  
  2172.                      Telecommunications                 page 47
  2173.  
  2174.  
  2175.  
  2176.  
  2177. A few notes on the ins and outs of telecommunications...
  2178.  
  2179. The DTR signal is frequently used to control the modem. When the
  2180. DTR is "raised" or "high", the modem knows that we're ready to
  2181. do something. When the DTR is "dropped" or "low", the modem
  2182. knows that we're not going to do anything. In most cases, this
  2183. tells it to hang up or disconnect the phone line. Some modems
  2184. may be set to ignore the DTR, in which case it will not
  2185. disconnect when the DTR is dropped. Usually this can be fixed by
  2186. changing a switch on the modem. On some modems, a short software
  2187. command may suffice.
  2188.  
  2189. The DTR is generally the best way to disconnect. The Hayes "ATH"
  2190. command is supposed to hang up, but it doesn't work very well.
  2191.  
  2192. The PBWiz comm handler makes sure the DTR is raised when TCInit
  2193. is used. It does not automatically drop the DTR when TCDone is
  2194. used, so you can keep the line connected in case another program
  2195. wants to use it. If this is not suitable, just use TCDTR to drop
  2196. the DTR. Your program must always use TCDone before it exits,
  2197. but it need only drop the DTR if you want it that way.
  2198.  
  2199. If you want to execute another program via SHELL, it is ok to
  2200. leave communications running as long as control will return to
  2201. your program when the SHELLed program is done. In that case, the
  2202. input buffer will continue to receive characters from the comm
  2203. port unless the SHELLed program provides its own comm support.
  2204. The output buffer will likewise continue to transmit characters
  2205. unless overruled.
  2206.  
  2207.                         Text-mode Video                  page 48
  2208.  
  2209.  
  2210.  
  2211. The graphical interface has become a "sexy" thing to have these
  2212. days, but there are still many good reasons to work in text
  2213. mode. It's relatively fast, it works on all monitors, and it's
  2214. often the most appropriate choice. Graphics is overkill for many
  2215. applications. Besides, you can't redirect graphics to a file or
  2216. to the printer.
  2217.  
  2218. Come to think of it, text displayed by the PRINT statement can't
  2219. be redirected either. Fortunately, we can fix that. All it takes
  2220. is sending the output through DOS:
  2221.  
  2222.    DosPrint St$         ' print to the current output device
  2223.  
  2224. A nice thing about DOS output is that ANSI display codes will
  2225. work if you have an ANSI driver (such as ANSI.SYS) installed. If
  2226. you're working with existing text, such as captured output from
  2227. a BBS or from an ANSI art program like TheDraw, you can just use
  2228. the DOSPrint routine to handle it. If you're looking to do your
  2229. own ANSI output, though, there's an easier way than looking up
  2230. the individual codes and sending 'em out one at a time. The
  2231. following routines send the appropriate ANSI codes to the
  2232. current DOS output device:
  2233.  
  2234.    DosCls                    ' clear the screen
  2235.    DosColor Fore%, Back%     ' set the screen colors
  2236.    DosLocate Row%, Column%   ' set the cursor position
  2237.  
  2238. The primary advantage of DOS output is that it can be
  2239. intercepted. It normally goes to the screen, but you can
  2240. redirect it to a file, printer, or comm port, among other
  2241. things. You can also be sure that DOS output will work
  2242. reasonably in a multitasking environment like DESQview or
  2243. Windows, instead of messing up the screen. The disadvantage is
  2244. that DOS output is fairly slow. It's great for command line
  2245. utilities, but not if you plan to do any fancy screen work. For
  2246. that, you probably want direct-access techniques.
  2247.  
  2248. The direct screen access provided by PBWiz is rude, crude... and
  2249. faster than a greased euphemism. It can't be redirected, doesn't
  2250. handle control codes, and won't even update the cursor position.
  2251. In return for this lack of amenities, it gives you two very
  2252. valuable things: complete control and raw speed.
  2253.  
  2254.                         Text-mode Video                  page 49
  2255.  
  2256.  
  2257.  
  2258. The direct-access replacement for PRINT works like so:
  2259.  
  2260.    XQPrint St$, Row%, Column%, Attr%
  2261.  
  2262. Now, you might guess that St$ is the text to print, and Row% and
  2263. Column% are where to print it. The Attr% may seem a bit more
  2264. opaque. The Attr% is the color to use-- actually, both the
  2265. foreground and background colors. These are combined into a
  2266. single value because that's the way the display controller wants
  2267. to see it. Remember, the emphasis here is on speed, not
  2268. necessarily convenience! So how do you calculate an attribute
  2269. given the foreground and background colors?
  2270.  
  2271.    Attr% = CalcAttr% (Fore%, Back%)
  2272.  
  2273. An Attr% value is never less than 0 or greater than 255-- it
  2274. will fit into a single byte. That may be useful to know for
  2275. storage purposes. In any event, keep the Attr% in mind, because
  2276. we'll be seeing more of it in the future. By the way, you can
  2277. unpack an attribute into foreground and background colors too,
  2278. if need be:
  2279.  
  2280.    UnCalcAttr Attr%, Fore%, Back%
  2281.  
  2282. In addition to XQPrint, there is a routine which allows you to
  2283. overlay existing text rather than replacing it. It skips blank
  2284. spaces rather than putting them on the display.
  2285.  
  2286.    XQPrintOver St$, Row%, Column%, Attr%
  2287.  
  2288. The direct-access routines allow for one, and only one, option
  2289. which might slow them down. The IBM CGA and some clone CGAs
  2290. flicker horribly if you access their display memory directly.
  2291. There is a software fix, but it slows the display down
  2292. tremendously. Still, if your program is run on such a CGA,
  2293. you'll want to stop the flickering. This may be done so:
  2294.  
  2295.    AntiSnow Slow%       ' any nonzero value to stop flicker
  2296.  
  2297. You should provide a command-line switch or configuration option
  2298. to force anti-flicker support. Don't do it by default, as the
  2299. slowdown is very noticeable.
  2300.  
  2301. The "menu" approach has become the standard way of allowing a
  2302. user to choose between various program options. There are many
  2303. ways of designing a menu, but they almost always require a
  2304. "highlight" to show the current choice. This highlight is
  2305. generally handled by changing the color of the chosen item:
  2306.  
  2307.    ReColorArea TopRow%, LftCol%, BotRow%, RtCol%, Attr%
  2308.  
  2309.                         Text-mode Video                  page 50
  2310.  
  2311.  
  2312.  
  2313. Pop-up windows have become ubiquitous. Naturally, PBWiz supports
  2314. them too:
  2315.  
  2316.    PopWindow TRow%, LCol%, BRow%, RCol%, Frame%,
  2317.       Attr%, Grow%, Shade%, TFore%, Title$   ' use one line!
  2318.  
  2319. The first four parameters specify the upper left corner and
  2320. lower right corner of the window. The window frame, if any, is
  2321. created just outside these coordinates. If you choose a shadow
  2322. for a 3D effect, that will extend further outside the window.
  2323. Keep this in mind if you want to save the part of the screen
  2324. under the window... but we'll get to that! Let's see what
  2325. options are available for the frame type:
  2326.     0   no frame
  2327.     1   single lines
  2328.     2   double lines
  2329.     3   single horizontal, double vertical lines
  2330.     4   double horizontal, single vertical lines
  2331.     5   block graphic lines
  2332.  
  2333. These are the available shadows:
  2334.    -3   transparent shadow on the right
  2335.    -2   transparent shadow on the left
  2336.    -1   solid black shadow on the left
  2337.     0   no shadow
  2338.    1+   shadow attribute (use CalcAttr) for a colored shadow
  2339.  
  2340. Options for growing windows are as follows:
  2341.    -1   grow as fast as possible
  2342.     0   pop onto the screen
  2343.    1+   grow with a specified delay in milliseconds
  2344.         (15 works well for me)
  2345.  
  2346. The TFore% parameter is the foreground color to use for the
  2347. title (Title$), if any (use "" for no title).
  2348.  
  2349. It is worth noting that the "milliseconds" value is only rather
  2350. approximate. The delay is based on the video card, and is fairly
  2351. similar on any computer system, but there will be a noticeable
  2352. difference between (say) an XT with a CGA and a 486 with a VGA.
  2353. Still, it's a useful delay-- if not really precise, it's at
  2354. least fairly accurate, and it has a fine resolution. You can
  2355. access this delay yourself:
  2356.  
  2357.    DelayV MilliSeconds%
  2358.  
  2359.                         Text-mode Video                  page 51
  2360.  
  2361.  
  2362.  
  2363. I mentioned screen saves a bit earlier. With PBWiz, you can save
  2364. any part of a screen, and restore it later to the same place or
  2365. an entirely different area. This also offers the possibility of
  2366. creating a screen image, storing it in a file, and reading it
  2367. into your program, among other things. It works like this:
  2368.  
  2369.    Scr$ = ScreenSave$ (TopRow%, LftCol%, BotRow%, RtCol%)
  2370.  
  2371.    ScreenRestore Scr$, TopRow%, LftCol%
  2372.  
  2373. It takes about 4K to store a full 80x25 text screen. The exact
  2374. calculation is Bytes = Rows * Columns * 2 + 2, if you care to
  2375. figure it out yourself. You can also take advantage of a PBWiz
  2376. routine to handle it:
  2377.  
  2378.    Bytes% = CalcSize% (TopRow%, LftCol%, BotRow%, RtCol%) + 2
  2379.  
  2380. Finally, we are left with a series of routines which let you
  2381. scroll (or clear) any part of the screen:
  2382.  
  2383.    ScrollDown TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2384.    ScrollLeft TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2385.    ScrollRight TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2386.    ScrollUp TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2387.  
  2388. If you attempt to scroll zero times, or more times than there
  2389. are rows (or columns, depending on which way you scroll), the
  2390. specified area of the screen will be cleared. The Attr% gives
  2391. the color to use on the cleared area of the screen.
  2392.  
  2393.                             Credits                      page 52
  2394.  
  2395.  
  2396.  
  2397. I'd like to thank Dave Navarro for letting me in on the world of
  2398. PowerBasic. His help was most valuable to me in many respects.
  2399. Without him, this library would have taken much longer to get
  2400. off the ground or would perhaps not even exist.
  2401.  
  2402. I would also like to thank Spectra, publishers of PowerBasic,
  2403. for sending me the evaluation copy of PowerBasic which led to my
  2404. decision to write this library. I have also found their tech
  2405. support to be outstanding and of great assistance.
  2406.  
  2407. Special thanks to Bob Zale, author of PowerBasic, for having
  2408. written a terrific compiler; and for service above and beyond
  2409. the call of duty.
  2410.  
  2411. My thanks also to the good people who have registered PBWiz.
  2412. You're the ones that make PBWiz happen.
  2413.  
  2414.