The CBIN package is a small collection of BIN routines that can be LOADed and CALLed by dBase III Plus. They also work with FoxBase+. The name "CBIN" comes from the fact that the routines are written, not in assembler as in usual for BIN routines, but in the C programming language. Russell Freeland modified the start-up code for Wizard C to work with dBase; Andrew Schulman wrote much of the C code. Whereas large packages such as dBase Tools for C or Tom Rettig's Library require that you load (and learn!) a large software package simply in order to perform a few functions, the CBIN routines operate independently of one another. Learn and load the ones you need, and you don't have to bother with a 100K memory-resident program hogging your PC's memory. For more information, contact either: Andrew Schulman Russell Freeland 12 Humboldt Street 1780 SW 43 Cambridge MA 02140 Fort Lauderdale FL 33064 We would appreciate hearing from you: bug reports would be particularly appreciated right now. Also, suggestions for other BIN routines that you would find useful in your everyday work. Finally, any monetary contribution you could make would be appreciated. We have big plans for more CBIN utilities, so it's worth your while to become a CBIN supporter. In the following descriptions of the BIN routines, note that all BINs must be LOADed into dBase III Plus before they can be CALLed. You can do a RELEASE MODULE when you are done with it. Also note that some of the BIN files have to be called twice: once to "seed" them with necessary information they need, and again to get back the information you need. WRITE: Write characters to file or to printer (even chr(0)) ----- Setup: F = " [+]" CALL WRITE WITH F : any valid DOS filename or PRN: (for printer) : x (for hexadecimal) d (for decimal) [+] : optional + sign indicates append to file, not overwrite Return from setup: if F = "-1" then file could not be opened; otherwise F will contain DOS file handle, e.g., "04" Use (after setup): CALL WRITE WITH "" : if hexadecimal mode was selected, hex designation for characters; if decimal mode was selected, chr() designation. Multiple occurrences of characters are designated with (bytes,number), where number will always be a decimal count. Strings (cannot include spaces) are delimited with apostrophes: 'hello' (See examples below) Close file: CALL WRITE WITH "(!)" Examples: To create a DBF file without using CREATE (more useful DBF files can then be constructed using COPY TO STRUCTURE EXTENDED and CREATE FROM commands): LOAD WRITE F = "FOO.DBF X" CALL WRITE WITH F && open the file IF (F <> "-1") && check if okay * write characters out to file CALL WRITE WITH "3 (0,7) 41 0 4f (0,21) 46 (4f,2) (0,8)" CALL WRITE WITH "43 (0,4) 4e (0,15) d 1a" ENDIF CALL WRITE WITH "(!)" && close the file USE FOO && do something with it Note that even though FOO.DBF was opened in hex mode, (0,21) writes 21 copies of chr(0) out to the file, not 33. In hex mode, only the characters themselves are in hex; counts are always in decimal. To set an Epson printer to italic, print the word "Hello" and return to roman (does not work in FoxBase+): CALL WRITE WITH "PRN: X" CALL WRITE WITH "1b '4' 'Hello' 1b '5' (!)" READ: Read a character from a file ---- Setup: F = "" CALL READ WITH F : : any valid DOS filename No value returned by setup. To call (after setup): C = SPACE(1) CALL READ WITH C Return from call: The next available character in file is returned in passed variable. If end-of-file is reached, or otherwise character can not be retrieved, chr(255) is returned. Close file: CALL READ WITH CHR(255) Problems: That READ is not parallel to WRITE is clearly a problem. This will be fixed in future versions. This version simply illustrates that you can have relatively fast character-at-a- time access to files from within dBase. Future versions will let you access multiple characters at a time, and will allow you to retrieve the next word from a file (similar to the C function strtok()). Examples: * TESTREAD.PRG PARAMETER FILE LOAD READ CALL READ WITH "&FILE" && open the file ? UPPER(FILE) ? C = SPACE(1) DO WHILE (C <> CHR(255)) && while not end-of-file CALL READ WITH C && get a char into C ?? C && display it ENDDO CALL READ WITH CHR(255) && close the file DO TESTREAD WITH "CBIN.TXT" && display this file This example merely displays a file on the screen. Presumably you would want to do something with the characters inside the do-while loop, other than just display them. PEEK: Examine PC memory ---- To call: M = " [count]" CALL PEEK WITH M : : address [count] : optional number of bytes to retrieve Return from call: After calling PEEK, M will contain the contents of PC memory address segment:offset. The number of bytes depends retrieved depends on the optional [count] parameter AND ON THE LENGTH OF THE STRING PASSED IN. Note that the information retrieved overwrites the information passed in. It is assumed that the information passed in is needed by the routine, not by the user. Problems and Comments: PEEK is very limited right now, as you need routines to retrieve valid addresses for you that can then be passed to PEEK. PEEK should have a "mode" in which the information retrieved for you is returned in a form that can then be passed back to PEEK. Any decent book on BASIC gives many illustrations of the wonderful non-portable tricks that can be done with a properly- written PEEK. Valid dBase addresses can be retrieved with VARPTR. (See below) Example: M = "68c1 d 11" CALL PEEK WITH M ? M "Hello world" POKE : Fondle PC memory ---- To call: CALL POKE WITH " [switch]" : : address : arbitrary number of bytes [switch] : optional: -d means that are in decimal, -a means ascii, default is hex. (See examples) No value returned by call. Problems and Comments: POKE suffers from the same problems as PEEK. Also note that PEEK and READ should be very similar in operation; and that POKE and WRITE should match each other as well. Example: The following examples all POKE "Hello" into the same address in memory: CALL POKE WITH "68c1 d 48 65 6c 6c 6e" CALL POKE WITH "68C1 000D H E L L O -A" CALL POKE WITH "68c1 000d 72 101 108 108 111 -d" Note that even when the are in decimal, addresses are always in hexadecimal. This is the convention in addressing memory. VARPTR -- Take address of dBase variable ------ Setup: X = CALL VARPTR WITH X No value returned by setup. To use: Y = CALL VARPTR WITH Y Notes: The segment returned is dBase's data segment. The dBase III Plus memvar symbol table can be found near :0004 (however, this is not the case in FoxBase+). Example: X = "Hello world" CALL VARPTR WITH X Y = SPACE(10) CALL VARPTR WITH Y ? Y 68c1 d Y = y + " 11" + SPACE(5) CALL PEEK WITH Y ? Y Hello world INTER -- do DOS or BIOS interrupt from dBase (yow!) ----- To use: Using this one is complex enough that a dBase PRG front end, INTER.PRG, has been provided. If you want to call INTER.BIN directly, see how INTER.PRG is coded. Otherwise, just call INTER.PRG or include it in your standard procedure file. The syntax for call INTER.PRG is: DO INTER WITH INTNO,AX,BX,CX,DX Note: Remember that if one of the "high" registers must be loaded (for instance, BH), the value must be multiplied by 256. See the sample program, TESTINT.PRG Return: See the sample program, TESTINT.PRG BEEP -- Sound the PC speaker ---- To use: CALL BEEP WITH , Notes: See the sample program, BACH.PRG, for an example of how to call beep. The Bach transcription into computer gibberish comes, more or less, from the Framework II Developer's Toolkit. The BEEP routines were derived from routines published in the February 1987 PC TECH JOURNAL with important corrections in the July 1987 letters column. RAND -- Generate pseudorandom number ---- Setup: X = CALL RAND WITH X To use: X = CALL RAND WITH X Notes: Unlike the other BIN routines, RAND takes and returns a single dBase numeric memory variable, and NOT its string representation. Example: . load rand . seed = val(substr(time(),7,2)) . call rand with seed . x = 0 . call rand with x . ? x 346 . call rand with x . ? x 130 . call rand with x . ? x 10982 . call rand with x . ? x 1090 FILEFIND -- Wildcard searches for files, directories -------- Setup: F = "(!) " CALL FILEFIND WITH F To use: F = "" CALL FILEFIND WITH F Notes: Each time you call FILEFIND, it will retrieve the next DOS file matching the pattern with which you "seeded" it. To tell FILEFIND you're starting "seeding" a new pattern, preface with pattern with "(!)". The attributes are passed as string representations of the following numbers: 0 - normal files 1 - read-only files 2 - hidden files 4 - system files 8 - volume label 16 - directory 32 - archive Example: F = "\*.* 16" + SPACE(15) CALL FILEFIND WITH F DO WHILE (F <> "-1") && until the well runs dry... ? F F = SPACE(15) CALL FILEFIND WITH F ENDDO This finds all directories and normal files off the main root. F = "(!)" + "*.BIN" + STR(0) + SPACE(15) DO WHILE (F <> "-1") ? F F = SPACE(15) CALL FILEFIND WITH F ENDDO Can you figure out what this does? SRCHPATH -- Search DOS path for file -------- See Russell's SRCHPATH.PRG, and all will be revealed unto you. -- Andrew Schulman 6 July 1987 P.S. Again the authors of CBIN implore you to send feedback and/or contributions.